diff --git a/safety-related-profile/README.md b/safety-related-profile/README.md index 749a7309..c8300d86 100644 --- a/safety-related-profile/README.md +++ b/safety-related-profile/README.md @@ -2,9 +2,7 @@ # ONNX Safety-Related Profile Working Group (WG) -This repository is where ONNX Safety-Related Profile WG will capture various artifacts and deliverables. - -The purpose of this WG is to elaborate an ONNX profile dedicated to safety-related systems. +The purpose of this WG is to elaborate an ONNX profile dedicated to "safety-related" systems. An ONNX model is the representation of an ML/AI model to be implemented. In other words, it is the specification for the implementation activity. The model may be either interpreted by a tool or translated into some lower level equivalent representation (e.g., some source code) by a tool or a human. In both cases, to be able to interpret the model according to the intent of its designer, its syntax and semantics must be clear and non-ambiguous. @@ -13,29 +11,39 @@ This is for instance the case in the aeronautical domain for which evidences sha We consider that the current ONNX standard does not fully satisfy these requirements and that there is a need (i) to clarify the industrial needs in that matter, (ii) to identify and address the weaknesses of the current standard in a systematic manner in order to produce an "safety-related ONNX profile" that would to fulfil these needs. We also consider that these needs are specific and that the proposed changes and clarifications of the syntax, semantics, and documentation shall not prevent the use of ONNX in domains where constraints are relaxed. For instance, introducing restrictions on the parameters values for operators of the safety-related profile must not affect the usage of the same operators out of the profile. -## Working Group Status +# Contents + +The main elements of this repository are: +- [Slides of the Kick-Off Meeting](./meetings/general/2024-09-25%20-%20KOM/2024-09-25%20-%20SONNX%20KOM.pdf) +- [Recent presentation of the projects results](./meetings/Other_meetings/SONNX%20-%20WG114-%20oct-2025.pdf) +- [Minutes of the WG meetings](./meetings/minutes.md) +- Deliverables (draft) + - [Industrial needs](./deliverables/needs/needs.md) (What are the needs of the industrial partners?) + - [Use cases](./deliverables/scope/scope.md) (What are the first models to be implemented using SONNX?) + - [Requirements](./deliverables/reqs/reqs.md) (How do the needs translate to constraints on the SONNX profile?) + - [Issues](./deliverables/issues/issues.md) (What are the first issues identified for the usage of ONNX in a safety-related system?) (obsolete) + - [Informal specification of operators](./sonnx/ops/spec/informal/) + - [Formal specification of operators](./sonnx/ops/spec/formal/) + Formal specification of operators is done using the [Why3](https://www.why3.org/). + - [Formal specification of graphs](./sonnx/graph/README.md) (tbc) +Note that this is a **work in progress**. + + +# Working Group Status **ACTIVE** -# Slack channel -Please sign up at https://slack.lfai.foundation/ and join [onnx-safety-related-profile-wg](https://app.slack.com/client/TPUCV58TG/CPS6Y3N21) channel. # WG Lead(s) -* Eric JENN (IRT Saint-Exupery) and Jean SOUYRIS (Airbus) (July 22, 2024 - Current) - -# Logistics +* Eric JENN (IRT Saint-Exupery, France) and Jean SOUYRIS (Airbus, France) (July 22, 2024 - Current) -* WG leads will drive the meeting. -* Meeting annoucement will be posted in our Slack channel: https://lfaifoundation.slack.com/archives/C02AANGFBJB -* Feedbacks and topic request are welcome by all. -* Documents and artifacts: https://github.com/onnx/working-groups/tree/main/safety-related-profile # WG Meeting Info -* Meeting (to be defined). -* TEAMS Meeting link: (to be defined) -* Meeting ID: (to be defined) - -# Meeting notes +* Working group meetings take place every other Wednesday, 4 p.m. to 6 p.m. (Paris time) +* To join, please subscribe to the SONNX mailing list (see below); we will come back to you. +* The meeting notes can be found [here](./meetings/minutes.md). -The meeting notes can be found [here](https://github.com/onnx/working-groups/tree/main/safety-related-profile/meetings) +# Mailing list +* To subscribe to the mailing list, send an email to onnx-sonnx-workgroup+subscribe@lists.lfaidata.foundation + diff --git a/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx b/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx new file mode 100644 index 00000000..8225acbb Binary files /dev/null and b/safety-related-profile/analyses/certification/SONNX_ED324_interest.docx differ diff --git a/safety-related-profile/attic/certification/SONNX_ED324_interest.docx b/safety-related-profile/attic/certification/SONNX_ED324_interest.docx new file mode 100644 index 00000000..9938fffe Binary files /dev/null and b/safety-related-profile/attic/certification/SONNX_ED324_interest.docx differ diff --git a/safety-related-profile/documents/mngt/README.md b/safety-related-profile/attic/mngt/README.md similarity index 100% rename from safety-related-profile/documents/mngt/README.md rename to safety-related-profile/attic/mngt/README.md diff --git a/safety-related-profile/documents/mngt/WG_objectives_and_organisation.docx b/safety-related-profile/attic/mngt/WG_objectives_and_organisation.docx similarity index 100% rename from safety-related-profile/documents/mngt/WG_objectives_and_organisation.docx rename to safety-related-profile/attic/mngt/WG_objectives_and_organisation.docx diff --git a/safety-related-profile/documents/mngt/Workplan.xlsx b/safety-related-profile/attic/mngt/Workplan.xlsx similarity index 100% rename from safety-related-profile/documents/mngt/Workplan.xlsx rename to safety-related-profile/attic/mngt/Workplan.xlsx diff --git a/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md b/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md new file mode 100644 index 00000000..a9cead1a --- /dev/null +++ b/safety-related-profile/attic/operator_spec_sub_wg/2025-11-07-work_session.md @@ -0,0 +1,38 @@ +### Notations and other conventions to be added to the *guidelines* +- Change the notation of variadic arguments from $X_i$ to $Xi$ . +- All elements are indexed from $0$ to $L$ , L being the index of the **L**ast element. +- In the spec of all operator, add an hyperlink to the ONNX operator +- To designate a specific element of a tensor $T$, use $T[i,j,..]$, i.e., square bracket rather than parentheses. +- Comply with the ONNX naming conventions: $add$ becomes $Add$. +- Use ONNX type lists (without the "tensor()) +- Provide a template of the informal spec that will serve as a basis for the writing of new operators (besides the *guidelines*) +- [ ] (all) Modify the already specified operators to ensure compliance with these new conventions. + +### Review of the $\text{Slice}$ operator +- See replies to Ricardo and Jõao's questions in [joao-ricardo-doubts.md](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/slice/reviews/joao-ricardo-doubts.md) +- Those questions raised very interesting discussions about empty tensors +- [ ] (Eric) Create a side note about "empty tensors" (To be placed in "doc"). [project::[[SONNX]]] +- Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.) + +### Review of the $\text{Clip}$ operator +- Jõao and Ricardo have questions about the handling of NaN. + - The spec. should only refer to $\min(M, \max(X[i],L))$ and define what $\min(x,NaN)$ and $\max(x,NaN)$ means. + - Consider other special values (+Inf, -Inf, $0^+$ +, $0^-$ ) + - Check what the IEEE says about min and max with these values. +- [ ] (Mariem) Give R&J a pointer to Why3 where NaN are handled. +- [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed). + +### Review of $\text{Max}$ and broadcasting +- See Eric's comments [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/max/reviews/max-ejn.md) +- [ ] (Jean-loup) Separate the spec broadcasting and the max operator + - Broadcasting is specified as $(Z1,Z2,ZL)=\text{bc}(X1,X2,...,XL)$ + - The spec is to be placed in "common" + - "Properties" will be useful to support the verification (formal, test) activities but they have to be removed from the informal spec. + - They should be specified in the most simple way so has to be traceable to some simple implementation using primitive operations (loops, simple mathematical operations). In particular, we should stick to two-step process : (i) construction of the broadcasted tensors and (ii) computation of the maxes on those tensors must be explicit. + +### Other topics +- [ ] (Eric) Provide explanations about the new way to manage modifications (using Pull Requests). +- [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$) +- [ ] (Eric) During next meeting ask participants if they know other issues similar to those raised by empty tensors. + + diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/README.md b/safety-related-profile/attic/operator_spec_sub_wg/README.md similarity index 100% rename from safety-related-profile/meetings/operator_spec_sub_wg/README.md rename to safety-related-profile/attic/operator_spec_sub_wg/README.md diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/SONNX_Operator_List.xlsx b/safety-related-profile/attic/operator_spec_sub_wg/SONNX_Operator_List.xlsx similarity index 100% rename from safety-related-profile/meetings/operator_spec_sub_wg/SONNX_Operator_List.xlsx rename to safety-related-profile/attic/operator_spec_sub_wg/SONNX_Operator_List.xlsx diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png new file mode 100644 index 00000000..071c598a Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/conv_netron.png differ diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png new file mode 100644 index 00000000..4244ad2f Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.drawio.png differ diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md new file mode 100644 index 00000000..f584ad00 --- /dev/null +++ b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/core_ops.md @@ -0,0 +1,333 @@ +# Introduction + +The idea is to build up all operators of our selection on the basis of a small corpus of primitive operators. The objective is to simplify the specification (esp. the formal specification) thanks to a modular approach. + +In some cases, the decomposition is fairly trivial and the resulting description remains understandable describes in a fairly understanable way what the operator is doing. However, care shall be taken not to make the description of the operators overly complicated. A good example is the one of convolution (`conv`). We will show how it can be decomposed using simpler operators, but at the expense of making it much more difficult to understand than when using the usual "triple-sum" (for a 2D convolution). + + +# Core operators +## Core Arithmetic Operators: + - Add, (Sub) + - Mul, Div + - Exp + - Sine, (Cos, Tan) + - Log + - Sqrt + +## Matrix Multiplication: +- MatMul + +## Data Manipulation/Shaping Operators: +- Reshape, +- Concat, +- Slice, +- Gather, +- Where, +- Shape + +## Comparison/Logical Operators +- Max, (Min), +- Greater, (Less). + +## Reduction Operators +- ReduceSum, +- ReduceMean, +- ReduceMax. + +## Other operators (non ONNX) +Those operators do not belong to the ONNX operator set. However, they are useful to describe the are not ONNX operators, but opera +- Broadcast, used to describe the implicit broadcasting done by some operators +- Dilation, used to describe the dilation done by the `conv` operator. + +# Synthesis + +The following table indicates is an operator is primitive or not. +When the answer is "YES (*)", it means that we consider it primitive even though they could actually be described using more primitive operators. This is for instance the case of `Sub(x,y)` which could be in principle encoded as `Add(x, -y)`. + +An empty entry means that the operator has not been analyzed yet. + +| Operator | Primitive | +|------------------------------|-----------| +| Abs | NO | +| Add | YES | +| Cast | | +| Clip | | +| Concat | YES | +| Constant | YES | +| ConstantOfShape | | +| Conv | NO | +| ConvTranspose | NO | +| Div | YES (*) | +| Equal | YES | +| Erf | YES | +| Exp | YES | +| Expand | | +| Flatten | NO | +| Gather | | +| Gemm | NO | +| GlobalAveragePool | NO | +| GRU | | +| HardSwish | NO | +| Identity | | +| LeakyRelu | NO | +| Less | | +| Log | YES | +| LSTM | | +| MatMul | YES | +| Max | YES | +| MaxPool | | +| Min | YES (*) | +| Mod | YES | +| Mul | YES | +| Neg | NO | +| Not | YES | +| Pad | YES | +| Pow | YES | +| Range | | +| ReduceMean | YES | +| ReduceSum | YES | +| Relu | NO | +| Reshape | YES | +| Resize | | +| ScatterND | | +| Shape | YES | +| Sigmoid | NO | +| Slice | | +| Softmax | NO | +| SoftPlus | NO | +| Split | | +| Sqrt | YES | +| Squeeze | | +| Sub | YES (*) | +| Tanh | NO | +| Transpose | NO | +| ConvTranspose | | +| Unsqueeze | | +| Where | | + + +# Examples + +# Abs +Operator Àbs`could be defined using operator `Max`: + +$$\texttt{Abs}(x) = \texttt{Max}(x,-x)$$ + +or + +$$\texttt{Abs}(x) = x \times \texttt{Sign}(x)$$ + + +## Neg + +$$Neg(x)= x \times -1$$ + +## ReLU + +$$Relu(x) = max(1,x)$$ + +## Convolution + - Conv = (Slice + Reshape = img2col) + MatMul + +A convolution can be implemented using a matrix multiplication by "flattening" the kernel and reorganizing the tensor, as illustrated below for a (2x2) kernel and (3x3) matrix. + +![concat decomposition](core_ops.drawio.png) + +The Slice operator is used to extract the four "patches". Then each patch is reshaped into a (1x4) vector. The four vectors are then concatenated into a 4x4 matrix. + +The kernel is reshaped (`reshape`) into a 1x4 vector. + +The flattened kernel is then multiplied with the 4x4 matrix. The (1x4) vector result is finally reshaped into a 2x2 matrix. The Python code can be found in [Google Collab](https://colab.research.google.com/drive/1CpJv2-zwHV_ZmC7FH9VD0UffFDahHI3s#scrollTo=vWPbx5Br7RJZ). + +The resulting graph is given below: +drawing + + +*WARNING: describing convolution this way makes the actual operation done by the operator much more difficult to understand... In fact, this scheme will necessarily be explained with a reference to the classical way to compute convolutions...* + +The `Conv` operator also does padding and dilation. Padding can be expressed using the ONNX `Pad` operator. since there is no Dilation operator, we have to describe it using simpler operators. + +Dilation can be implemented using `Reshape` and `Concat`. For instance, for a 2D tensor: +- First, we insert zeroes between columns by + - reshaping the tensor to add a dimension. This means that the scalar items of the matrix becomes vectors. The initial shape 2x2 becomes 2x2x1 : + ``` + [ + [1,2], + [3,4] + ] + ``` + becomes + ``` + [ + [ [1],[2]], + [ [3],[4]] + ] + ``` + - we construct a zero tensor with the same shape 2x2x1: + ``` + [ + [ [0],[0]], + [ [0],[0]] + ] + ``` + - Then we concat the two tensors along dimension 2. The resulting tensor is now 2x2x2 + ``` + [ + [[1,0],[2,0]], + [[3,0],[4,0]] + ] + ``` + - Then we reshape the tensor to 2x4 which is the size of the dilated tensor along the second dimension (columns): + ``` + [ + [1,0,2,0], + [3,0,4,0] + ] + ``` + - Second, the same process is applied on the rows. + +We could also use `pad` to insert the zeroes. For instance, if we consider the dilation on the column dimension: +- First, as before, we reshape the tensor from 2x2 to 2x2x1 +- We pad the tensor with zeroes (tensor is now 2x2x2): + ``` + [ + [[1,0],[2,0]], + [[3,0],[4,0]] + ] + ``` +- and we reshape it to 2x4 + ``` + [ + [1,0,2,0], + [3,0,4,0] + ] + ``` + +We could also use `Reshape` to flatten the tensor, then use `ScatterElement`to insert zeroes and use `Reshape` again to set final tensor shape. + +## MaxPool + + - MaxPool = Slice + ReduceMax + Concat + +## LayerNormalization + - LayerNormalization = ReduceMean, Sub, Pow, Sqrt, Add, Div, Mul + +## GlobalAveragePool + +*Defined with respect to AveragePool.* + +## AveragePool + +`AveragePool` can be defined using a channel-wise convolution. Average pooling consists to apply a kernel which values are all equal to $1/\texttt{kernel size}$. + + +It can also be described using `Slice`, `Reshape` and `ReduceMean` + +## HardSwish + +Operator `HardSwish` can be defined using the `max` and `min` operators: + +$$ hardswish(x) = x * max(0, min(1, \alpha * x + \beta))$$ + +with $\alpha=1/6$ and $\beta=0.5$. + +It could also be expressed using `Relu` or `HardSigmoid`. + +## LeakyRelu + +The mathematical definition of `LeakyRelu` is + +$$ LeakyRelu(x) = \begin{cases} + x & \text{if}\ x \ge 0 \\ + \alpha x & \text{if}\ x \lt 0 + \end{cases} $$ + +which can be written as + +$$LeakyRelu(x) = max(0,x)+min(0, \alpha x)$$ + +## SoftMax + +Operator `SoftMax` is defined as follows: +$$\texttt{SoftMax}(x_i) = { e^{x_i} \over {\sum_{j=1}^{K} e^{x_j}}}$$ + +The numerator can be computed using operator `Exp`. The denominator can be computed using the `ReduceSum` operator that computes the sum of the input tensor’s elements along the provided axes. + +In order to ensure numerical stability, the actual formula should be + +$$\texttt{SoftMax}(x_i) = { e^{x_i-max(x)} \over {\sum_{j=1}^{K} e^{x_j-max(x)}}}$$ + +which means that we have to use the `ReduceMax` operator. + +The netron representation of the `SoftMax` operator is given below: + +drawing + +## SoftPlus + +Operator `SoftPlus` is defined as follows: +$$\texttt{SoftPlus}(x) = \ln(e^{x} +1 )$$ + +*Note that in the ONNX documenation, $\ln$ and $\log$ are used. There is a unique logarithm operator, `Log` that computes the natural (neperian) logarithm, i.e, $\log(x)=\ln(x)=\log_e(x)$.* + +This operator can be described using `Log` and `Add`. + +## Sigmoid + +The mathematical expression for operator `Sigmoid` is : + +$$ sigmoid(x)= { 1 \over 1+e^{-x} } $$ + +This operator can be described using `Div`, `Exp` and `Add`. + +## Tanh + +The mathematical expression for operator `Tanh` is : + +$$ tanh(x)= { e^x-e^{-x} \over e^x+e^{-x} } $$ +or +$$ tanh(x)= { 1-e^{-2x} \over 1+e^{-2x} } $$ + + +This operator can be described using `Div`, `Exp`, `Add`, `Sub`, `Mul` + +## Transpose + +Transpose could be expressed using +- `Reshape` (or `Flatten`), to flatten the tensor, +- a computation of the new indices +- `Gather` to collect the elements of the flattened tensor +- `Reshape` to set back the dimension of the tensor + +## Flatten + +Operator `Flatten` flattens a tensor starting from a given axis. If input tensor has shape $(d_0, d_1, … d_n)$ then the output will have shape $(d_0 \times d_1 \times ... \times d_{{axis}-1}, d_{axis} \times d_{{axis}+1} \times ... \times d_n)$. + +This operator can be represented using: +- `Shape` to get the shape of the tensor +- `Slice`, `ReduceProd`, and `Concat` to compute the flattened dimensions +- `Reshape` + +The following picture gives an example of such transformation applied to a 2D input tensor (but it can be generalized). + +Values `dim0` and `dim1` correspond to the previous products: +- ${dim_0} = d_0 \times d_1 \times ... \times d_{{axis}-1}$ +- ${dim_1} = d_{axis} \times d_{{axis}+1} \times ... \times d_n$ + + +drawing + +Operator `Slice` uses the starts, ends, axes and steps inputs to select a sub-tensor of its input data tensor. (see the ONNX and numpy documenration: it is a pretty complicated operator...) + +## Gemm + +Operator `Gemm` is defined as follows: +$$\texttt{Gemm}(A,B,C) = \alpha \times A' \times B’ + \beta \times C$$ + +where $A'$ and $B'$ are the transposed version of $A$ and $B$ depending on attributes `transA` and `transB`. + +This operator can be described using operators `Transpose`, `MatMul`, `Add`, `Mul`. + +The booleans that control the transposition of $A$ and $B$ are attributes, not input tensors. Therefore, the description of the operator should probably (?) use a textual condition, (i.e., "if transA is true then...") rather than other operators. (To be discussed). + + diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png new file mode 100644 index 00000000..d26673cc Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/flatten_netron.png differ diff --git a/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png new file mode 100644 index 00000000..07653a59 Binary files /dev/null and b/safety-related-profile/attic/operator_spec_sub_wg/core_ops/softmax_netron.png differ diff --git a/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md b/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md new file mode 100644 index 00000000..4fb89111 --- /dev/null +++ b/safety-related-profile/attic/operator_spec_sub_wg/dumitru-2025-07-21.md @@ -0,0 +1,54 @@ +**Dumitru's proposal concerning broadcasting** + +ONNX has pretty complex broadcasting rules, described +here: + https://onnx.ai/onnx/repo-docs/Broadcasting.html +They claim they follow Numpy's rules on most operators, +as defined here: + + https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules + +and a different rule on Gemm and on Prelu. + +But both rules are pretty complex. + +> (Eric) They are not *** that *** complex but, for sure, adding broadcasting will make our life more complicated. +>> (Dumitru) It's arguably more complex than the operators that use it, like "add". + +Thus, it draws attention away from the core functionality of the operator to the broadcasting. + +The question is: how to define the *semantics* of operators that perform broadcast? Add the (explicit) description of broadcasting to all operators performing it, or defined the operator semantics separately, and the broadcast separately? + +One first remark here is that actually decomposing inside ONNX for implementation purposes does not seem to be possible, as no explicit broadcast operator exists. This is unlike in numpy, where functions exist both for the broadcast of a tensor to a shape: + + https://numpy.org/doc/stable/reference/generated/numpy.broadcast_to.html#numpy.broadcast_to + +and for computing the broadcast shape. + + https://numpy.org/doc/stable/reference/generated/numpy.broadcast_shapes.html#numpy.broadcast_shapes + +However, for semantics definition purposes, my suggestion would be to decompose by introducing a "virtual" broadcast operator (e.g. why3 functions corresponding to numpy.broadcast_to, numpy.broadcast_shapes) and then defining the full semantics of the operator by composing this with a simple definition of the base operator. + +> (eric) As you write above, the other solution is to specify the operation including the broadcasting, as it will >be actually done in the implementation. This will make the mathematical formulation and the formal spec significantly more complicated because we will have to play with mods whenever we compute an index. + +>> (Dumitru) Exactly. + +Something like: +``` +x = onnx.add(y,z) +``` +would become: +``` + x_shape = broadcast_shapes(shape(y),shape(z)) + y_full = broadcast_to(y,x_shape) + z_full = broadcast_to(z,x_shape) + x = add_homogenous(y_full,z_full) +``` + +> (Eric) I fully agree with the approach. +> We may possibly add a predicate "need broadcasting" so that we can clearly express in which condition broadcasting is nedeed and possible. This will anyway be necessary to express the usage constraints. +So, I propose that we keep the capability of broadcasting (for the moment we can keep our spec as is and we will add broadcasting in a second phase). + +> (Eric) Note that concerning `x_shape = broadcast_shapes(shape(y),shape(z))`, this raises another question : we have also forbidden shape inference. Normally, the shape of x should be known in advance... + +>> (Dumitru) Oh, then it's even easier, requiring in the operation properties that the constant x_shape provided by the programmer satisfies "x_shape = broadcast_shapes(shape(y),shape(z))" diff --git a/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md b/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md new file mode 100644 index 00000000..4ca15554 --- /dev/null +++ b/safety-related-profile/attic/operator_spec_sub_wg/error_conditions.md @@ -0,0 +1,140 @@ +# Introduction + +This document is a followup to our July 2nd discussion about the way to specify the behaviour of operators in the presence of errors conditions. + +# The issue +Some operator are not defined for some input values. Typical examples are division by zero, overflows, etc. + +For simple operators, those conditions can be specified in the definition of the input domain. For instance `y = Div (a , b)` is not defined for $b=0$, and the domain of the inputs in $\mathbb R$ or $\mathbb Z$ is $\mathbb R\times \mathbb R^*$ or $\mathbb Z\times \mathbb Z^*$, respectively. + +For complex operators, the correct domain (where the function is defined) can't be defined easily due to the complexity of the relation between the error condition (e.g., some denominator equal to 0 or some overflow) and the inputs. + +**How can we address this in our informal specification?** + +For instance, let's take the `Add` operator whose signature is: + +`y : int32 = Add(a: int32, b: int32)` + +It can be specified as + +$y = a+b$ where operator $+$ is the usual mathematical addition defined in $\mathbb R$, $\mathbb Z$, etc. + +Several options for the specification can be proposed... + +# The options + +#### Option 0 +**Don't say anything...** + +The operator `Add` is simply specified as +$$y = a + b $$ + +Problem: this specification is incorrect since, as soon as $a+b > 2^{32}$, the value $a+b$ does not belong to the set of integers `INT32` +. + +The user may hopefully be in the case where the condition $a+b\leq 2^{32}$ holds, but the specification remains incorrect or inconsistent (i.e., the "user" of the operator cannot expect condition $y=a+b$ to be satisfied for all values in the domain). + +#### Option 1 +**Specify the exact conditions (the domain) for which the specification is applicable.** + +Here, the condition is $a+b \leq 2^{32}-1$ (for the right bound of the domain) where, again, operator "$+$" is the usual addition. + +The specification becomes +$$y = a + b $$ +for any $a$ and $b$ in $\mathbb Z \times \mathbb Z$ such that +$-2^{32} \leq a+b \leq 2^{32}-1$ + +This solution makes sense but it is not really practical since to check the condition *in practice*, we have to compute the result in $\mathbb Z$)... + +In practice, we would be happier with some condition - possibly more conservative - easier to check. + +For instance, for a simple addition, this could be : $-2^{31}+1\leq a \leq 2^{31}-1$ and $-2^{31}+1 \leq b \leq 2^{31}-1$ . + +However, this approach is not easy to implement for more complex operators. It **may** work for instance if we know that the input values are in a certain range (e.g., because they have been normalized) so that not overflow can occur. + +#### Option 2 +**Specify the operator in the set `INT32`, i.e., using signed, two's complement arithmetic.** + +Expressed using the usual $+$ operator, the specification becomes +$$ +y = +\begin{cases} +(a + b) \bmod 2^n & \text{if } (a + b) \bmod 2^n < 2^{n-1} \\ +(a + b) \bmod 2^n - 2^n & \text{otherwise} +\end{cases} +$$ + +Advantage: the operation is fully and precisely defined in `INT32`. It matches exactly what happens in an implementation where our `INT32` isa 32 bit machine value. + +Problem: This approach could be applied to simple operators but for more complex operator such as, for instance, a matrix multiplication, all intermediate operations used to describe the matrix multiplication would actually refer to operation in `INT32`. The usual $+$ will be replaced by $(+)$, etc. This approach is cumbersome and does not really express what we expect. In practice, we would probably prefer all intermediate "computations" to be carried out in $\mathbb Z$ and the final result to be mapped to `INT32` (to be compared with the case where all intermediate operations are carried out in $\mathbb Z$). + +Another solution would be to do all computations using standard arithmetic in $\mathbb Z$ (i.e., use $+$ in the usual addition in $Z$) and require that no "intermediate value" ever gets out of the `INT32`domain. + +In that case, it'll be the responsibility of the implementer or the user to ensure that all these "intermediate values" remain in the appropriate domain: +- The implementer could perform actual (2's arithmetic) intermediate computations in a larger domain (tahn `INT32`) in order to prevent overflows +- The user could perform some verification (by testing, formal verification, etc.) to ensure that all intermediate values actual remain in the `INT32` domain. + +Note that the concept of "intermediate values" is strange in a specification. In a specification, when we write $y=a+b+c+d$, we do not say anything about the order in which the operations shall be actually performed (thanks to commutativity and associativity). We just express that the result $y$ must be equal to $a+b+c+d$ which, for the usual operator $+$ and values in $\mathbb Z$, can be done in any possible way ($a+(b+c+d))$, $((a+b)+(c+d)$), etc. + +### Option 3 +**Specify that the semantics is the one defined using the usual $+$ operator considering that no overflow shall occur (some would write "no runtime error occurs").** + +Such specification is a bit of a chicken-egg problem in the sense that saying the "no overflow shall occur" is not a complete specification: what are those "overflows"? what is the actual conditions to be checked? + +Stated differently, the specification is something abstract and we don't really know "when" those "overflow" may occur. + +If we were to be more explicit, we could (for instance) require that, in the case of operation $y=a+b+c+d$ : +- $a+b$ is in the domain of `INT32` +- $b+c$ is in the domain of `INT32` +- $c+d$ is in the domain of `INT32` +- $a+c$ is in the domain of `INT32` +- etc. + +because we simply don't know in which order the operations will be carried out. + +### Option 4 +**Simply state that "some error condition may occur" (e.g., an overflow). +** + +This is strange to write such a thing in a specification... because (i) it is unclear and (ii) brings no useful information (besides the one of being "careful"). + +### Option 5 +**Chose the most accurate option among the previous ones that can be implemented in practice for a given operator.** + +In the example of an operator that can lead to a division by zero, we would either say that "some division by zero could occur" (option 4) or "division by zero will occur for such or such input value" (option 1). + +# An illustration + +Let's consider the `MatMulInteger` operator. +This operator computes a matrix multiplication with input tensors in `int8` and output tensor in `int32`. + +This operator does not overflow "most of the time" thanks to the width of the accumulator. However, it **can** overflow if the **sizes** of the input tensors are sufficiently large. + +Consider the multiplication of two tensors `A` and `B` of shape `[1,N]` and `[N,1]`. The smallest $N$ for which overflow can occur is such that +$$ +127 \times 127 \times N > 2^{31} - 1 +$$ + +this gives +$$ +N > \frac{2^{32}-1}{127^2} \approx 133141.5 +$$ + +Since N is integer, the minimal value for an error to trigger is +$$ +N = 133142 +$$ + +Strangely (?), overflow occurs for $N=133145$. + +As shown below, both python reference implementation and ORT give the same (incorrect) result: + +``` +ONNX python reference Output: [-2147471591] +ONNX Runtime Output (int32): -2147471591 +Expected value: 2147495705 +``` + +The specification of `MatMulInteger` could state a condition on the shapes of `A` and `B` to prevent overflows. The condition only concerns the dimension of accumulation: if the dimension is smaller than 133145, no overflow can occur. + +As stated above, we may also specify the multiplication using 2's complement arithmetic. This specification would give the exact (but unexpected) result. \ No newline at end of file diff --git a/safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md b/safety-related-profile/attic/operator_spec_sub_wg/worksharing.md similarity index 56% rename from safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md rename to safety-related-profile/attic/operator_spec_sub_wg/worksharing.md index 8c85bbae..225c3f65 100644 --- a/safety-related-profile/meetings/operator_spec_sub_wg/worksharing.md +++ b/safety-related-profile/attic/operator_spec_sub_wg/worksharing.md @@ -1,4 +1,18 @@ -|Bigram | Actual name | +This document gives +- the list of operators to be covered by the SONNX working group +- the list of people involved in the **writing** and **review** processes +- the status of each operator + +If you want to contribute, please: +- add your name + initials in the "list of contributors" +- add your initial in the list of "writers" and/or "local reviewers". + +*Thanks!* + + +### List of contributors + +|Initials | Actual name | |------|-------------| | ej | Eric | | mt | Mariem | @@ -7,20 +21,38 @@ | tb | Thiziri | | hb | Henri | | nv | Nicolas | +| sml| Salomé | +| js | Jean | + +### Definition of statuses + +|Status | Meaning| +|-------|--------| +| DR | Draft | +| RLR-i | Ready for local (*) review #i | +| RGR-i | Ready for general (**) review #i | +| RER-i | Ready for external review #i | +| FI | Finalized | + +- (*) A "local" review involves a limited set of people. +- (**) A "general review" involves the complete working group. +- (***) An "external" review involves people outside of the working group. + +### Status of operators -| Operator | Writers | Reviewers | -|------------------------------|--------------------|-------------------| -| Abs | | | -| Add |hb?, |hb | +| Operator | Writers | Local reviewers | Status (WR, RW, FI) +|------------------------------|--------------------|-------------------|------------------- +| Abs |hb, | | DR +| Add |hb | sml | DR | Cast | | | | Clip | | | -| Concat | | | -| Constant | |hb | +| Concat |sml | | RGR-1 +| Constant |hb, | | DR | ConstantOfShape | | | -| Conv |ej,mt |jlf,sb,tb,hb | +| Conv |ej,mt |jlf,sb,tb,hb | RGR-1 | ConvTranspose | | | | Dense | | | -| Div |hb?, |hb | +| Div |hb, | | DR | Equal | | | | Erf | | | | Exp | | | @@ -28,46 +60,45 @@ | Flatten | | | | FullyConnected | | | | Gather | | | -| Gemm |nv | | +| Gemm |nv | | DR | GlobalAveragePool | | | | GRU | | | | HardSwish | | | | Identity | | | | LeakyRelu | | | -| Less |hb?, |hb | -| Log | | | -| LSTM | | | -| MatMul |nv | | -| Max | | | -| MaxPool | | | +| Less |hb, | | DR +| Log |hb, | | DR +| LSTM |nv | | DR +| MatMul |nv | | DR +| Max |jlf | | +| MaxPool |sml, js | | | Min | | | | Mod | | | -| Mul |hb?, |hb | -| Neg | | | +| Mul |hb, | | DR +| Neg |hb, | | DR | Not | | | | Pad | | | -| Padding | | | -| Pow | | | +| Pow |hb, | | DR | Range | | | | ReduceMean | | | | ReduceSum | | | -| Relu | | | +| Relu |sml, js | | | Reshape | | | -| Resize | | | +| Resize |sml | | | ScatterND | | | | Shape | | | -| Sigmoid | | | +| Sigmoid | | | DR | Slice | | | | Softmax | | | | SoftPlus | | | | Split | | | -| Sqrt | | | +| Sqrt |hb, | | WR | Squeeze | | | -| Sub |hb?, |hb | -| Tanh | | | +| Sub |hb, | | WR +| Tanh | | | WR | Transpose | | | | ConvTransposeDeconvolution | | | | Unsqueeze | | | -| Where |hb?, |hb | +| Where |hb, | | WR diff --git a/safety-related-profile/attic/profile_ir/IR.md b/safety-related-profile/attic/profile_ir/IR.md new file mode 100644 index 00000000..4b9979bf --- /dev/null +++ b/safety-related-profile/attic/profile_ir/IR.md @@ -0,0 +1,549 @@ + + +# Open Neural Network Exchange Intermediate Representation (ONNX IR) Specification + +__Purpose__ + +This document contains the normative specification of the semantics of ONNX. + +The `.proto` and `.proto3` files found under the [onnx folder](/onnx/) form the normative specification of its syntax authored in the [Protocol Buffers](https://developers.google.com/protocol-buffers) definition language. Commentary found in the `.proto` and `.proto3` files are intended to improve readability of those files, but are not normative if they conflict with this document. Such conflicts should be reported as documentation bugs. + +__Notes on model validation__ + +A [tool](../onnx/checker.py) is available to perform general validation of models against this specification. It is implemented in C++ with a Python command-line wrapper. + +__Notes on language in this and all related documents__: + +1. The use of SHOULD, MUST, MAY and so on in this document is consistent with [RFC 2119](https://www.ietf.org/rfc/rfc2119.txt). + +2. The use of 'list' shall denote an ordered collection of items, 'set' shall denote an unordered collection of unique elements, and 'bag' an unordered collection of possibly non-unique elements. + +## Components + +ONNX is an open specification that consists of the following components: + +1) A definition of an extensible computation graph model. + +2) Definitions of standard data types. + +3) Definitions of built-in operators. + +#1 and #2 together make up the ONNX Intermediate Representation, or 'IR', specification which is covered herein; the built-in operators are covered in documents listed at the end. Specifically, built-in operators are divided into a set of primitive operators and functions. A function is an operator whose semantics is formally expressed via expansion into a sub-graph (called the function body) using other operators (and functions). Functionality-wise, an ONNX compatible framework or runtime may inline a function body to execute it if it does not have corresponding implementation of the function. + +There are two official ONNX variants; the main distinction between the two is found in the default operator sets. __ONNX-ML__ extends the __ONNX__ operator set with ML algorithms that are not based on neural networks. + +Up to IR version 6, the ONNX specification and model format addressed only inference (also known as scoring). Starting from IR version 7, the ONNX specification and model format also support training. An ONNX training model is an extension of the inference-model. An inference-only runtime can consume a training model ignoring the training-related extensions. However, an inference-only model may enable a representation that is more optimal for inference purposes than a training model. + +## Runtime Agnostic + +ONNX does not pre-suppose or imply any particular method of runtime implementation. + +For example, an implementation may consist of a rich runtime which interprets the model; it may be a code generator that translates the model in its entirety to executable code for some target programming language; it may be a hardware implementation; it may be a combination of two or three of those. + +Nothing in this specification should be construed as advocating one implementation approach over any other; any comments on the inner workings of concrete implementations are to be interpreted as examples. + +## ONNX Versioning + +The IR specification, individual models, and operator sets are all versioned. Furthermore, each individual operator indicates which version of its containing operator set it was introduced or stabilized in. + +Version numbers can be used as a simple number, or used to encode [semantic versions](https://semver.org/)(AKA SemVer). If using semantic versions, the convention is to use the two most significant bytes for the major number, the next two bytes for the minor number, and the least significant four bytes for the patch/build/bugfix number. When using semantic versioning, at least one of the major/minor numbers MUST be non-zero. + +The IR specification uses simple monotonically increasing numbers for its versions. The valid IR versions are defined by the `onnx.Version` enumeration in [onnx.proto](/onnx/onnx.proto). + +Operator sets use a simple version number. Each operator set version represents a snapshot of the set of operators, and their semantics at a particular point in time. + +This specification does not provide guidance on what versioning scheme model producers should be using. + +More details on conventions and best practices for versioning of IR, operator sets, and models can be found in [Versioning](Versioning.md). + +## Extensible computation graph model + +ONNX specifies the portable, serialized format of a computation graph. It does not have to be the form a framework chooses to use internally. For example, an implementation may represent the model differently in memory if it is more efficient to manipulate during optimization passes. + +An implementation MAY extend ONNX by adding operators expressing semantics beyond the standard set of operators that all implementations MUST support. The mechanism for this is adding operator sets to the `opset_import` property in a model that depends on the extension operators. + +### Models + +The top-level ONNX construct is a ‘Model.’, and is represented in protocol buffers as the type `onnx.ModelProto` + +The main purpose of the model structure is to associate metadata with a graph which contains all the executable elements. The metadata is used when first reading the model file, giving an implementation the information it needs in order to determine whether it will be able to execute the model, generate logging messages, error reports, etc. Further, the metadata is useful to tools, such as IDEs and model galleries, which need it for informing humans about a given model’s purpose and characteristics. + +Each model has the following components: + +|Name|Type|Description| +|---|---|---| +|ir_version|int64|The ONNX version assumed by the model.| +|opset_import|OperatorSetId|A collection of operator set identifiers made available to the model. An implementation must support all operators in the set or reject the model.| +|producer_name|string|The name of the tool used to generate the model.| +|producer_version|string|The version of the generating tool.| +|domain|string|A reverse-DNS name to indicate the model namespace or domain, for example, 'org.onnx'| +|model_version|int64|The version of the model itself, encoded in an integer.| +|doc_string|string|Human-readable documentation for this model. Markdown is allowed.| +|graph|Graph|The parameterized graph that is evaluated to execute the model.| +|metadata_props|map|Named metadata values; keys should be distinct.| +|training_info|TrainingInfoProto[]|An optional extension that contains information for training.| +|functions|FunctionProto[]|An optional list of functions local to the model.| + + Models MUST specify a domain and use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html). + +__Note: Exploring an ONNX file__ + +You can use the `protoc` tool that is part of the Protocol Buffers distribution to examine the contents of an ONNX file, you do so like this: + +``` +$ protoc --decode=onnx.ModelProto onnx.proto < yourfile.onnx +``` + +Where [onnx.proto](/onnx/onnx.proto) is the file that is part of this repository. + +Alternatively, you can use a tool like [Netron](https://github.com/lutzroeder/netron) to explore the ONNX file. + +### Model Semantics + +The semantics of an inference-model is a _stateless function_ (except possibly for the state used for random-number generation). Thus, whenever an inference-model (without random-generator operations) is used to perform inference on the same input, it is expected to produce the same output. + +The semantics of a training model is that of a _stateful object_, with the state consisting of the current values of trained-weights (and any other auxiliary state required, such as momentum, for example, used by the learning algorithm). Specifically, its semantics is captured via three methods: an initialization method (which is used to initialize or reset the values of state variables), a training step method (to train using a batch of input-output pairs), and an inference method to perform inference using the current values of the learned weights. The first two methods update the state of the object, while the third method is side-effect-free. + +### Optional Metadata + +The 'metadata_props' field in the model is available for any kind of optional metadata that a tool or model developer chooses to place there. The following are the defined “standard” optional metadata properties of a model. + +Name|Type|Format|Description +|---|---|---|---| +model_author|string|A comma-separated list of names.|The personal name of the author(s) of the model, and/or their organizations. +model_license|string|Name or URL.|The well-known name or URL of the license under which the model is made available. + +### Operator Set Identifiers + +Each operator set is uniquely identified by a (domain, version) pair. + +Name|Type|Description +|---|---|---| +domain|string|The domain of the operator set being identified. +version|int64|The version of the operator set being identified. Same as 'opset_version' in the operator set. + +The operator set version is a simple integer value that is monotonically increased as new versions of the operator set are published. + +Operator sets other than the default operator set MUST specify a domain and SHOULD use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html). + +### Operator Sets + +Each model MUST explicitly name the operator sets that it relies on for its functionality. Operator sets define the available operators and their version. Each model defines the imported operator sets by their domains. All models implicitly import the default ONNX operator set. + +Each operator set SHALL be defined in a separate document, also using protobuf as the serialization format. How operator set documents are found at runtime is implementation-dependent. + +__Note: As of the publication of this document, no ONNX implementation is known to process operator set documents.__ + +The properties of an operator set are: + +Name|Type|Description +|---|---|---| +magic|string|The value ‘ONNXOPSET’ +ir_version|int32|The ONNX version corresponding to the operators. +ir_version_prerelease|string|The prerelease component of the SemVer of the IR. +ir_build_metadata|string|The build metadata of this version of the operator set. +domain|string|The domain of the operator set. Must be unique among all sets. +opset_version|int64|The version of the operator set. +doc_string|string|Human-readable documentation for this operator set. Markdown is allowed. +operator|Operator[]|The operators contained in this operator set. + +The operator set version is a simple integer value that is monotonically increased as new versions of the operator set are published. + +Operator sets other than the default operator set MUST specify a domain and SHOULD use reverse domain names based on the responsible organization's identity, the same convention that is used for [naming Java packages](https://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html). + +### Operators + +Each operator used within a graph MUST be explicitly declared by one of the operator sets imported by the model. + +The properties of an operator definition are: + +Name|Type|Description +|---|---|---| +op_type|string|The name of the operator (case sensitive), as used in graph nodes. MUST be unique within the operator set’s domain. +since_version|int64|The version of the operator set when this operator was introduced. +status|OperatorStatus|One of ‘EXPERIMENTAL’ or ‘STABLE.’ +doc_string|string|A human-readable documentation string for this operator. Markdown is allowed. + +The version value MUST be the same value as the operator set version when the operator was first published. Subsequent versions of the operator set MUST NOT alter the signature or semantics of the operator once published as STABLE. + +The ‘status’ property indicates whether the syntax, semantics, or presence of the operator is in an experimental or stable stage. Once an operator is published as STABLE, it’s syntax and semantics MUST NOT change in subsequent versions of the operator set. + +There are two distinct ways to pass information to operators – inputs and attributes. Inputs represent graph inputs or values computed elsewhere in the graph, while attributes are used for values that are constants in the graph. This distinction may be highly relevant to achieving good performance for some implementations, while completely irrelevant to others. + +### Functions + +A _function_ may be thought of as an operator combined with an implementation of the operator using +other, more primitive, ops, referred to as the _function body_. The function body consists of a +topologically sorted list of nodes that form a graph. Thus, a function combines aspects of both +an operator as well a graph (described below). + +Each function contained in a Model (also referred to as a model-local function) serves +as a default or fallback implementation of the corresponding operator. A runtime, however, +may choose to use an alternative implementation of the operator (usually as an optimized kernel). +As such, the unique name of a function is significant as it is implicitly associated with a +semantic specification. + +A serialized function (a _FunctionProto_) has the following properties: + +|Name|Type|Description| +|---|---|---| +name|string|The name of the function +domain|string|The domain to which this function belongs +overload|string|Part of unique id of function (added in IR version 10) +doc_string|string|Human-readable documentation for this function. Markdown is allowed. +attribute|string[]|The attribute parameters of the function +attribute_proto|Attribute[]| (IR version 9+) The attribute parameters with default values of the function. A function attribute shall be represented either as a string attribute or an Attribute, not both. +input|string[]|The input parameters of the function +output|string[]|The output parameters of the function. +node|Node[]|A list of nodes, forming a partially ordered computation graph. It must be in topological order. +|opset_import|OperatorSetId|A collection of operator set identifiers used by the function implementation. +|value_info|ValueInfo[]| (IR version >= 10) Used to store the type and shape information of values used in the function. +|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct. + +The name and domain serve to identify the operator uniquely in IR versions upto 9. IR version 10 adds the +field overload, and the triple (name, domain, overload) acts as a unique-id across functions stored in +a model. This is intended to support cases where distinct function-bodies are required for distinct +calls to the function within the model. +An opset version is not explicitly identified in a FunctionProto, but it is implicitly determined by +the opset version of the domain included in the model. + +The input, output, attribute, and attribute_proto (added in IR version 9) constitute the signature part of the operator. No type information +is explicitly included in the signature. The attribute_proto field describes attribute parameters of the function along with their default-value (when not specified by an call-site node), while the attribute field lists attribute parameters without a default-value. The names in these two lists must be distinct. When an attribute-parameter of the function is used in a node within the function, it is replaced by the actual parameter value specified for the attribute at a call-site node (of the function) when such a attribute is specified, and it is replaced by the default-value if the attribute has a default-value specified, and it is omitted otherwise. + +The opset_import and node fields describe the implementation of the function. + +The value_info field (added in IR version 10) allows a model to store type and shape information about the values used in a function, including its inputs and outputs. Note that this is optional, and ONNX allows functions to be polymorphic. + +### Graphs + +A graph is used to describe a side-effect-free computation (function). +A serialized graph is comprised of a set of metadata fields, a list of model parameters, and a list of computation nodes. + +Each computation dataflow graph is structured as a topologically sorted list of nodes that form a graph, which MUST be free of cycles. Each node represents a call to an operator or a model local function. Each node has zero or more inputs and one or more outputs. + +Graphs have the following properties: + +|Name|Type|Description| +|---|---|---| +name|string|The name of the model graph. +node|Node[]|A list of nodes, forming a partially ordered computation graph based on input/output data dependencies. It is in topological order. +initializer|Tensor[]|A list of named tensor values. When an initializer has the same name as a graph input, it specifies a default value for that input. When an initializer has a name different from all graph inputs, it specifies a constant value. The order of the list is unspecified. +doc_string|string|Human-readable documentation for this model. Markdown is allowed. +input|ValueInfo[]|The input parameters of the graph, possibly initialized by a default value found in ‘initializer.’ +output|ValueInfo[]|The output parameters of the graph. Once all output parameters have been written to by a graph execution, the execution is complete. +value_info|ValueInfo[]|Used to store the type and shape information of values that are not inputs or outputs. +|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct. + +ValueInfo has the following properties: + +Name|Type|Description +|---|---|---| +name|string|The name of the value/parameter. +type|Type|The type of the value **including shape information**. +doc_string|string|Human-readable documentation for this value. Markdown is allowed. + +Each main (top-level) graph MUST define the names, types and shapes of its inputs and outputs, which are specified as ‘value info’ structures. The main graph inputs and outputs are required to have a shape, indicating the rank, even though the exact dimensions need not be specified. + +Nested subgraphs (specified as attribute values) MUST define the names of its inputs and outputs +and MAY define the types of its inputs and outputs. + +Each graph MUST specify a name. + +The graph MUST adhere to single static assignment (SSA) for all node outputs; this means that all node output names MUST be unique within a graph. + +Graphs SHOULD be populated with documentation strings, which MAY be interpreted using GitHub-style markdown syntax. HTML and other text-markup languages MAY NOT be used in documentation strings. + +### Names Within a Graph + +All names SHOULD adhere to [C90 identifier syntax rules](https://en.cppreference.com/w/c/language/identifier). + +Names of nodes, inputs, outputs, initializers, and attributes are organized into several namespaces. Within a namespace, each name MUST be unique for each given graph. Please see below for further clarification in the case where a graph contains nested subgraphs (as attribute values). + +The namespaces are: + +Namespace|Description +|---|---| +Attribute|The names of attributes of an operator. Unique for each operator. +Value|The names of values – node inputs & outputs, tensor values (if named), graph inputs, outputs. +Node|The names of graph nodes. +Graph|The names of graphs within a domain, unique within the model domain. +Operator|The names of operators within a domain. +Shape|The names of tensor shape variables – scoped to the value information records of a graph, which is where shape variables occur. + + +### Nodes + +Computation nodes are comprised of a name, the name of an operator that it invokes, a list of named inputs, a list of named outputs, and a list of attributes. + +Input and outputs are positionally associated with operator inputs and outputs. Attributes are associated with operator attributes by name. + +They have the following properties: + +Name|Type|Description +|---|---|---| +name|string|An optional name of the node, used for diagnostic purposes only. +input|string[]|Names of the values used by the node to propagate input values to the node operator. It must refer to either a graph input, a graph initializer or a node output. +output|string[]|Names of the outputs used by the node to capture data from the operator invoked by the node. It either introduces a value in the graph or refers to a graph output. +op_type|string|The symbolic identifier of the operator to invoke. +domain|string|The domain of the operator set that contains the operator named by the op_type. +attribute|Attribute[]|Named attributes, another form of operator parameterization, used for constant values rather than propagated values. +doc_string|string|Human-readable documentation for this value. Markdown is allowed. +overload|string|Part of unique id of function (added in IR version 10) +|metadata_props|map|(IR version >= 10) Named metadata values; keys should be distinct. + +A name belonging to the Value namespace may appear in multiple places, namely as a graph input, a graph initializer, a graph output, a node input, or a node output. The occurrence of a name as a graph input, a graph initializer, or as a node output is said to be a definition and the occurrence of a name as a node input or as a graph output is said to be a use. + +A value name used in a graph must have a unique definition, with the exception that the same name MAY appear in both the graph input list and graph initializer list. (Further exceptions apply in the presence of nested subgraphs, as described later.) + +When a name appears in both the initializer list and the graph input list, a runtime MAY allow a caller to specify a value for this (input) name overriding the value specified in the initializer and a runtime MAY allow users to omit specifying a value for this (input) name, choosing the value specified in the initializer. Names of constants that are not meant to be overridden by the caller should appear only in the initializer list and not in the graph input list. In models with IR version >= 4, in nested subgraphs used as attribute values, users MUST NOT use the same name as both a subgraph initializer and subgraph input unless the corresponding op's specification explicitly allows it. In models with IR version <= 3, users MAY use the same name as both a subgraph initializer and subgraph input, but this is restricted to support constants via initializers that are not intended to correspond to any actual inputs passed from the node into the subgraph. In particular, the control-flow operator semantics determines the set of inputs supplied to the execution of the subgraph, and these input names MUST NOT appear as subgraph initializers. Subgraph initializer names must appear in the graph input list _after_ the actual inputs. This allows the actual inputs and formal inputs to be matched positionally. + +Edges in the computation graph are established by outputs of one node being referenced by name in the inputs of a subsequent node. + +The outputs of a given node introduce new names into the graph. The values of node outputs are computed by the node's operator. Node inputs MAY refer to node outputs, graph inputs, and graph initializers. When the name of a node output coincides with the name of a graph output, the graph output's value is the corresponding output value computed by that node. A node input in a nested subgraph MAY refer to names introduced in outer graphs (as node outputs, graph inputs, or graph initializers). + +The graph MUST use single static assignment for all node outputs, which means that all node output names MUST be unique within a graph. In the case of a nested subgraph, a node output name MUST be distinct from the names from the outer scopes that are visible in the nested subgraph. + +Node dependencies MUST NOT create cycles in the computation graph. + +The number of inputs and outputs in a node, their types, the set of attributes specified in a node and their types MUST satisfy the constraints imposed by the signature of the node’s operator. + +The list of nodes defining the top-level computation graph MUST be ordered topologically; that is, if node K follows node N in the graph, none of the data inputs of N may refer to outputs of K. + +Node attributes are used to pass literal (static) values to operators. + +#### Input and Output Values + +The representation distinguishes between two kinds of values: attribute values, which are statically known, and input/output values. The types of values permitted in the two cases are different. + +Input and output values are found as graph inputs, outputs, and initializers, and as node inputs and outputs. Their values are determined at runtime, either by the code that initiates model execution, or by operators computing output values. + +#### Attributes + +Attribute values are only found in nodes, passed to operators by name association. Attribute values are runtime constants, in that their values are determined when a model graph is constructed and therefore not computed at runtime. A common use for attributes is to represent coefficients established during model training. + +Attributes have the following properties: + +Name|Type|Description +|---|---|---| +name|string|The name of the attribute. Must be unique among attributes, inputs, and outputs for any given operator and node. +doc_string|string|Human-readable documentation for this value. Markdown is allowed. +type|AttributeType|The type of the attribute, determining which of the remaining fields is used to hold the value of the attribute. +f|float|A 32-bit floating-point value. +i|int64|A 64-bit integer value. +s|byte[]|UTF-8 string. +t|Tensor|A tensor value. +g|Graph|A graph. +floats|float[]|A list of 32-bit floating-point values. +ints|int64[]|A list of 64-bit integer values. +strings|byte[][]|A list of UTF-8 strings. +tensors|Tensor[]|A list of tensor values. +graphs|Graph[]|A list of graphs. +ref_attr_name|string|The name of a parent function's attribute. + +The properties ‘name’ and ‘type’ are required on all attributes, and ‘doc_string’ SHOULD be used on all attributes. An attribute MUST have only one of the value-carrying properties. + +In case ‘ref_attr_name’ is set, this attribute does not contain data, and instead it's a reference to the parent function's attribute of the given name. Can only be used within the function body. + + +#### Variadic Inputs and Outputs + +The last input or output of an operator MAY be marked as variadic. For example, the operator 'Max()' can be used to compute the maximum of a varying number of input values. A variadic operator has a minimum arity, which specifies the minimum number of operands that must be specified. + +For each variadic operator input, N or more node inputs must be specified where N is the minimum arity of the operator. For each variadic operator output, N or more node outputs must be specified where N is the minimum arity of the operator. + +#### Optional Inputs and Outputs + +##### Static Optional + +Some operators have inputs that are marked as optional, which means that a referring node MAY forgo providing values for such inputs. + +Some operators have outputs that are optional. When an actual output parameter of an operator is not specified, the operator implementation MAY forgo computing values for such outputs. + +There are two ways to leave an optional input or output unspecified: the first, available only for trailing inputs and outputs, is to simply not provide that input or output; the second method is to use an empty string in place of an input or output name. + +Each node referring to an operator with optional outputs MUST provide a name for each output that is computed and MUST NOT provide names for outputs that are not computed. + +Optional inputs and outputs of the above kind are referred to as _static-optional_. + +##### Dynamic Optional (since IR-8) + +**IR-8 Version** introduced a new type-constructor to represent _dynamic-optional_ inputs and outputs, +in addition to the earlier static-optional version described above. A dynamic-optional INT64 +tensor is a distinct type from an INT64 tensor type. In contrast, a static-optional INT64 +tensor does not have a distinct type, it has the same type as a INT64 tensor. +The operators `Optional` and `OptionalGetElement` MUST be explicitly used to convert between +the dynamic-optional type and the underlying non-optional type. +The dynamic-optional allows for more expressiveness than static-optional. + +#### External Tensor Data + +The raw data for large constant tensors, such as initializers, MAY be serialised in a separate file. In such a case, the tensor MUST provide the filename relative to the model file and MUST NOT use the value fields. It MAY provide a byte offset and length within that file. It MAY also specify a SHA1 digest of the file. One file MAY contain the data for multiple tensors. + +More details can be found in [External Data](ExternalData.md). + +## Standard data types + +There are two official ONNX variants; the main distinction between the two is found in the supported types and the supported operators. + +With respect to supported types, both __ONNX__ and __ONNX-ML__ definition recognize tensors, sparse tensors, sequences, maps, and optionals as input and output types. Sequences and maps were supported from the IR version 6 (ONNX 1.6.0 release). Optional type was supported from IR vesion 8 (ONNX 1.10.0 release). + +The following data types are supported by ONNX for inputs and outputs of graphs and nodes as well as the initializers of a graph. + +Primitive numeric, string, and Boolean types MUST be used as elements of tensors. + +### Tensor Definition + +Tensors are a generalization of vectors and matrices; whereas vectors have one dimension, and matrices two, tensors can have any number of dimensions, including zero. A zero-dimensional tensor is logically equivalent to a scalar value. + +Mathematically, a tensor can be defined as a pair of sequences/lists (V, S) where S is the shape of the tensor (a list of non-negative integers) and V is a list of values with length equal to the product of the dimensions in S. Two tensors (V, S) and (V', S') are equal if and only if V = V' and S = S'. The length of S is referred to as the rank. + + - If S has length 0, V must have length 1, since the empty product is defined to be 1. In this case, the tensor represents a scalar. + - S can contain dimensions of value 0. If any dimensions are 0, V must have length 0. + - If S has length 1, V has length equal to the single dimension in S. In this case, the tensor represents a vector. + - A tensor representing a vector of length 1 has shape [1], while a tensor representing a scalar has shape []. They both have a single element, but scalars are _not_ vectors of length 1. + +A tensor's shape S is a list but can be represented as a tensor with values S and shape [R] where R is the rank of the tensor. + + - For a tensor (V, S), the tensor representing its shape is (S, [R]). + - The shape of a scalar is []. Represented as a tensor, [] has shape [0]. + +#### Representation + +It is common to represent a tensor as a nested list. This generally works fine, but is problematic when zero dimensions are involved. A tensor of shape (5, 0) can be represented as [[], [], [], [], []], but (0, 5) is represented as [] which loses the information that the second dimension is 5. + + - A nested list is not a complete representation of a tensor with dimensions of value zero. + +### Tensor Element Types + +|Group|Types|Description| +|---|---|---| +Floating Point Types|float16, float32, float64, bfloat16, float8e4m3fn, float8e5m2, float8e4m3fnuz, float8e5m2fnuz, float4e2m1|Values adhering to the IEEE 754-2008 standard representation of floating-point data or defined in papers [FP8 Formats for Deep Learning](https://arxiv.org/abs/2209.05433), [8-bit Numerical Formats for Deep Neural Networks](https://arxiv.org/abs/2206.02915), and the [Open Compute Project](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf) +Signed Integer Types|int4, int8, int16, int32, int64|Signed integers are supported for 4-64 bit widths. +Unsigned Integer Types|uint4, uint8, uint16, uint32, uint64|Unsigned integers are supported for 4-64 bit widths. +Complex Types|complex64, complex128|A complex number with either 32- or 64-bit real and imaginary parts. +Other|string|Strings represent textual data. All strings are encoded using UTF-8. +Other|bool|Boolean values represent data with only two values, typically true and false. + +### Input / Output Data Types + +The following types are used to define the types of graph and node inputs and outputs. + +|Variant | Type | Description | +|---|---|---| +ONNX|dense tensors|Represents a Tensor. See definition above. +ONNX|sequence|Sequences are dense, ordered, collections of elements that are of homogeneous types. +ONNX|map|Maps are associative tables, defined by a key type and a value type. +ONNX|optional|Optionals are wrappers that may contain an element of tensor, sequence, or map type, or may be empty (containing none). [Details](ONNXTypes.md) + +#### Static tensor shapes + +In addition to element type, tensor types have a **static** shape. The static shape of a tensor variable is related to, but different from, the runtime (dynamic) shape of a tensor value. A static tensor shape is a list of records that indicates whether the tensor is a vector, a matrix, or a higher-dimensional value. For example, a 100x100 matrix has the shape [100,100]. + +The static shape is defined by 'TensorShapeProto': + +``` +message TensorShapeProto { + message Dimension { + oneof value { + int64 dim_value = 1; + string dim_param = 2; + }; + }; + repeated Dimension dim = 1; +} +``` +Which is referenced by the Tensor type message: + +``` + message Tensor { + optional TensorProto.DataType elem_type = 1; + optional TensorShapeProto shape = 2; + } +``` + +The empty list of dimension sizes, [], is a valid tensor shape, denoting a zero-dimension (scalar) value. A zero-dimension tensor is distinct from a tensor of unknown dimensionality, which is indicated by an absent 'shape' property in the Tensor message. When the shape property is absent in the type of a value (including node input), +it indicates that the corresponding runtime value may have any shape. This sub-section describes how to interpret a missing-shape or a shape with missing dimensions etc. However, specific usage contexts may impose further constraints on a type and shape. +For example, the inputs and outputs of a model (top-level graph) are required to *have* a shape, indicating the rank of inputs and outputs, +even though the exact dimensions need not be specified. + +Each size in the list MAY be expressed as an integral value or as a "dimension variable," a string denoting that the actual size of the dimension is not statically constrained to a particular number. This is useful for declaring interfaces that care about the number of dimensions, but not the exact size of each dimension. A dimension MAY have neither dim_value nor dim_param set. Such a dimension represents an unknown dimension unrelated to other unknown dimensions. + +For example, a NxM matrix would have the shape list [N,M]. + +The name of each dimension variable SHOULD adhere to [C90 identifier syntax rules](https://en.cppreference.com/w/c/language/identifier). + +Currently, dimension variables are not scoped. A dimension variable "N" represents the same value across the entire graph in a model. For example, if the graph has two inputs X and Y each with shape ["N"], then at runtime the values passed in for X and Y MUST be tensors of rank 1 with the same dimension. Nested sub-graphs currently share the same scope for dimension variables as the main-graph. This allows a model to relate the dimensions of tensors inside the subgraph to the dimensions of tensors in the outer graph. + +ONNX supports types such as Sequences of Tensors. The global scoping of dimension variables means that a variable with type "Sequence" represents a sequence of tensors that *all have the same shape*. The dimension variables M or N must be omitted from the above type if that dimension does not have a fixed size across all tensors in the sequence. The entire shape must be omitted from the type if different tensors in the sequence may have different ranks. + +For example, a graph that performs matrix cross-product may be defined as taking two inputs of shape [K,M] and [M,N], and producing an output of shape [K,N]. + +Shapes MAY be defined using a combination of integers and variables. + +_Historical Notes_: The following extensions were considered early on, but were never implemented or supported. +* The use of an empty string (as a dimension variable) to denote an unknown dimension not related to any other dimension. This was discarded in favor of using a Dimension with neither dim_value nor dim_param set. +* The use of the string "\*" (as a dimension variable) to denote a sequence of zero or more dimensions of unknown cardinality. This is not supported. In the current implementation, the number of dimensions in a shape MUST represent the rank of the tensor. A tensor of unknown rank is represented using a TypeProto::Tensor object with no shape, which is legal. +* A scoping mechanism to allow dimension variables that are local to a sub-graph (such as the body of a loop) may be useful, but is not currently supported. +* ONNX supports types such as Sequences of Tensors. A scoping mechanism for the dimension variables local to a type may be useful to distinguish between the following two types: a sequence of square matrices (of differing sizes) vs a sequence of square matrices (all of same size). This is not currently supported. + +### Attribute Types + +The type system used for attributes is related to but slightly different from that used for of inputs and outputs. Attribute values may be a dense tensor, sparse tensor, a scalar numerical value, a string, a graph, or repeated values of one of the above mentioned types. + +## Other Metadata + +The ModelProto structure, and in IR versions >= 10, various other structures (GraphProto, FunctionProto, NodeProto) +contain a metadata_props field allowing users to store other metadata in the form of key-value pairs. +It is recommended that users use key names qualified with a reverse-DNS name as prefix +(such as "ai.onnxruntime.key1") to avoid conflicts between different uses. +Unqualified names may be used in the future by the ONNX standard. + +## Training Related Information + +Training related information is described by one or more instances of _TrainingInfoProto_ contained in a model. Each TrainingInfoProto contains information describing both an initialization step and a training step. + +The initialization step is described using a Graph (TrainingInfoProto.initialization) and an initialization-binding map (TrainingInfoProto.initialization_binding). The initialization step is performed by evaluating the Graph, and assigning the outputs produced by the Graph to the _state variables_ of the training model as specified in the initialization-binding. The initialization-binding is conceptually a map, specified as a list of key-value pairs, where each key is the name of a state variable, and the value is the name of an output of the (initialization) Graph. Each name specified as a key in the binding MUST be the name of an initializer that appears in the main inference graph (i.e., in ModelProto.graph.initializer) or the name of an initializer that appears in TrainingInfoProto.algorithm.initializer. Each name specified as a value in the binding MUST be the name of an output of the TrainingInfoProto.initialization graph. Key values specified in the repeated initialization_binding field MUST be unique. + +The training step is similarly described using a Graph (TrainingInfoProto.algorithm) and an update-binding map (TrainingInfoProto.update_binding). The training step is performed by evaluating the Graph and assigning the outputs produced by the Graph to the state variables as specified in the update-binding. The constraints and description presented above for the initialization apply to the training step as well. + +Thus, the state variables of the training model consist of a subset of the initializers of the main inference graph (i.e., ModelProto.graph.initializer) and the training-algorithm graph (TrainingInfoProto.algorithm.initializer) as identified by the keys of the bindings (in TrainingInfoProto.initialization_binding and TrainingInfoProto.update_binding). Note that the state variables are not constant values in the context of training. They represent mutable variables shared by multiple graphs (implicitly declared in the top-level training model scope). This implicit declaration of shared mutable variables is used instead of an explicit declaration for purposes of backward compatibility with the inference graph representation. + +All state variables are pre-initialized to the value specified in the corresponding initializer. A subsequent call to perform the initialization step (using the appropriate API exposed by a runtime) updates the values of the state variables as described above. If the training model has more than one instance of TrainingInfoProto, the initialization step corresponding to each is performed in order. A TrainingInfoProto.initialization MAY be omitted (only if there are no initialization_bindings). For the training step, a runtime MAY allow users to invoke any one of the TrainingInfoProto.algorithm, allowing the training process to interleave the different algorithms as desired. The order in which the different TrainingProto.algorithms are called affects the training result, and it is the callers responsibility to call them in the correct order. + +## Other Specification Documents + +The ONNX specification is comprised of this document, which defines the semantics of the IR and the standard data types, and the following documents defining standard operator semantics and the IR syntax. The latter is specified as Protobuf v2 and v3 schema files. + +See the [metadata category documentation](MetadataProps.md) for more details. + +### Operators + +[Neural Network Operators](Operators.md) + +[Classical Machine Learning operators](Operators-ml.md) + +### Syntax + +[ONNX Models and Graphs - protobuf v2](../onnx/onnx.proto) + +[ONNX Models and Graphs - protobuf v3](../onnx/onnx.proto3) + +[ONNX-ML Models and Graphs - protobuf v2](../onnx/onnx-ml.proto) + +[ONNX-ML Models and Graphs - protobuf v3](../onnx/onnx-ml.proto3) + +[ONNX Operator Sets - protobuf v2](../onnx/onnx-operators.proto) + +[ONNX Operator Sets - protobuf v3](../onnx/onnx-operators.proto3) + +[ONNX-ML Operator Sets - protobuf v2](../onnx/onnx-operators-ml.proto) + +[ONNX-ML Operator Sets - protobuf v3](../onnx/onnx-operators-ml.proto3) + +### Versioning Conventions and Best Practices + +[Versioning](Versioning.md) \ No newline at end of file diff --git a/safety-related-profile/attic/profile_opset/README.md b/safety-related-profile/attic/profile_opset/README.md new file mode 100644 index 00000000..27869bff --- /dev/null +++ b/safety-related-profile/attic/profile_opset/README.md @@ -0,0 +1,5 @@ +## Operators specification + +- **Informal Specification:** [Link](../../sonnx/ops/spec/informal) +- **Formal Specification:** [Link](../../sonnx/ops/spec/formal) +- **Guidelines for Informal Specification:** [Link](../../sonnx/ops/docs/guidelines/informal.md) \ No newline at end of file diff --git a/safety-related-profile/documents/conv_specification_example/reviews/README.md b/safety-related-profile/attic/profile_opset/conv/reviews/README.md similarity index 91% rename from safety-related-profile/documents/conv_specification_example/reviews/README.md rename to safety-related-profile/attic/profile_opset/conv/reviews/README.md index 76d3bc06..0e9ee04f 100644 --- a/safety-related-profile/documents/conv_specification_example/reviews/README.md +++ b/safety-related-profile/attic/profile_opset/conv/reviews/README.md @@ -1,4 +1,4 @@ -This directory contains the reviews of the **CONV2D operator** description. +This directory contains the reviews of the **CONV2D operator** description and tests. | Id | Who | |----------|---------------------------| diff --git a/safety-related-profile/meetings/reqs_sub_wg/README.md b/safety-related-profile/attic/reqs_sub_wg/README.md similarity index 99% rename from safety-related-profile/meetings/reqs_sub_wg/README.md rename to safety-related-profile/attic/reqs_sub_wg/README.md index 7fcfe4d1..f63e53ce 100644 --- a/safety-related-profile/meetings/reqs_sub_wg/README.md +++ b/safety-related-profile/attic/reqs_sub_wg/README.md @@ -1,2 +1,2 @@ -This is the work area for the "requirement definition sub workgroup": -- list of operators needed to cover the [Use Cases](../../documents/usecases.md), in this [Excel sheet](./ONNX_operators_for_Use_Cases.xlsx). +This is the work area for the "requirement definition sub workgroup": +- list of operators needed to cover the [Use Cases](../../documents/usecases.md), in this [Excel sheet](./ONNX_operators_for_Use_Cases.xlsx). diff --git a/safety-related-profile/meetings/analysis_sub_wg/minutes.md b/safety-related-profile/attic/reqs_sub_wg/minutes.md similarity index 100% rename from safety-related-profile/meetings/analysis_sub_wg/minutes.md rename to safety-related-profile/attic/reqs_sub_wg/minutes.md diff --git a/safety-related-profile/meetings/reqs_sub_wg/slides-18-12.md b/safety-related-profile/attic/reqs_sub_wg/slides-18-12.md similarity index 100% rename from safety-related-profile/meetings/reqs_sub_wg/slides-18-12.md rename to safety-related-profile/attic/reqs_sub_wg/slides-18-12.md diff --git a/safety-related-profile/meetings/team.md b/safety-related-profile/attic/team.md similarity index 100% rename from safety-related-profile/meetings/team.md rename to safety-related-profile/attic/team.md diff --git a/safety-related-profile/deliverables/issues/issues.md b/safety-related-profile/deliverables/issues/issues.md index 1fc8dfff..179090bd 100644 --- a/safety-related-profile/deliverables/issues/issues.md +++ b/safety-related-profile/deliverables/issues/issues.md @@ -5,7 +5,8 @@ - CAT: _category in {__OPERATOR__ , __GRAPH__ , __FORMAT__}_ - CRIT: _criticality in { __LOW__ , __HIGH__ }_ - REQ: _Identification of the SONNX requirement that can't be satisfied due to this issue_ -- LOC: _Location in the standard, possibly using an hyperlink__ +- LOC: _Location in the standard, possibly using an hyperlink_ +- AUT: _author_ (optional) ### Issue _Description of the issue (in a synthetic way)_ @@ -19,38 +20,14 @@ _Proposal to solve the issue or mitigate its consequences_ ### Remarks (opt) _Additional remarks_ -# Issues - Non operators -## Issue #1.1: Execution order of graph nodes -- CAT: GRAPH -- CRI: LOW -- REQ: (TBC) -- LOC: [Open Neural Network Exchange Intermediate Representation (ONNX IR) Specification](https://github.com/onnx/onnx/blob/main/docs/IR.md) - -### Issue -The ONNX specification states that "Each computation dataflow graph is structured as a topologically sorted list of nodes that form a graph, which MUST be free of cycles. [...] ". The topological order is a partial order. - -### Consequence -Different implementations may execute nodes in different orders, leading to different results when computations are done using floating poit numbers. - -### Proposal -The SONNX standard should provide a means to impose a total ordering on the nodes. - -### Remarks -This constraint will prevent optimisations. -Note that nothing prevents a model to be ill-formed. Compliance with the syntax and semantics of the ONNX standard must be checked (it is certainly checked, but nothing is said about what is checked or not and whether these checkers are complete / correct or not). - -Other constraints are given in the [onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3). E.g.: - - // One FunctionProto can reference other FunctionProto in the model, however, recursive reference - // is not allowed. - -## Issue #1.2: Overloading +## Issue #1.1: Overloading - CAT: (to be completed) - CRIT: (to be completed) -- REQ: (to be completed) -- LOC: ONNX file format definition ([onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3)) +- REQ: (to be completed) +- LOC: ONNX file format definition ([onnx-ml.proto3](https://github.com/onnx/onnx/blob/main/onnx/onnx-ml.proto3)) +- AUT: (to be completed) ### Issue A [function in ONNX](https://onnx.ai/onnx/intro/concepts.html) is a way to reuse the same combination of operators at different locations in a model. A function may refer to operators that are in a different opset than the model itself. In that case, the standard leaves the runtimes the freedom to chose whether the local @@ -165,7 +142,6 @@ IR version 10 introduces overloading, i.e. the capability to have several defini ### Issue Variadic operators can accept any number of inputs. - ### Consequence (TBC) @@ -233,158 +209,167 @@ Do we need to specify our own encoding format? ONNX supports __Quantization__ operators. Quantization data types are not consistent accross operators. [QuantizeLinear](https://onnx.ai/onnx/operators/onnx__QuantizeLinear.html) is able to output int16, uint16, but [QLinearMatMul](https://onnx.ai/onnx/operators/onnx__QLinearMatMul.html), [QLinearConv](https://onnx.ai/onnx/operators/onnx__QLinearConv.html), [MatMulInteger](https://onnx.ai/onnx/operators/onnx__MatMulInteger.html) and [ConvInteger](https://onnx.ai/onnx/operators/onnx__ConvInteger.html#l-onnx-doc-convinteger) do not support these types. -# Operators -## Issue #2.1: Incomplete specification of SPLIT operator +## Issue #1.11: Axis attributes -- CAT: Operator -- CRIT: High -- REQ: (TBC) +- CAT: Format +- CRI: LOW +- REQ: (TBC) - LOC: https://onnx.ai/onnx/operators/onnx__Split.html - + https://onnx.ai/onnx/operators/onnx__Concat.html + https://onnx.ai/onnx/operators/onnx__Softmax.html + https://onnx.ai/onnx/operators/onnx__Slice.html + ### Issue -1. The "axis" attribute gives an integer to define the axis along which split the input tensor into a list of tensors. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor. Is the following interpretation the correct representation ? : +Several operators identified in CNNs present the « axis » attribute (ex : Split, Concat, Softmax, Slice). This attribute gives an integer to define the axis of the input tensor on which the operation applies. But the onnx documentation does not specify the correspondance between the integer and the axis of the tensor. Is the following interpretation the correct representation ? - axis = '0' <=> batch ? - axis = '1' <=> channels ? - axis = '2' <=> rows ? - axis = '3' <=> columns ? - -2. Moreover, the "split" input gives a tensor which indicate the length of the 'axis' specified for each output tensor. For example, if the shape of the input tensor is [1,32,320,320] (assuming 32=channels, 320=lines and 320=columns) and the "split" input is [16,16] with the "axis" attribute = 1, then the operator splits the input tensor into two output tensors [1,16,320,320] and [1,16,320,320]. In general, the next layer of the network applies its operation on one of the two output tensors and the other one is kept for use in a deeper layer of the network. But the documentation does not specify which 16 channels are used in the next layer and which 16 channels are set aside. Is it the first 16 or the last 16 channels of the input tensor ? +If the « axis » attribute is set to ‘1’, does this mean that the channels of the input tensors are operated (concatenated or splitted or sliced for example) ? ### Consequence -1. When implementing a neural network containing a "split" operator, the split operation on an input tensor can be performed on the wrong axis et so it would give an incorrect result. -2. After the split of tensor's channels, the next layer of the neural network could receive the wrong feature maps and not those expected from the correct channels. +If the operation was performed along the wrong axis of the input tensors, the values of elements in the feature maps of the output tensor could be incorrect and then the next layer of the CNN would give an incorrect result by applying its operation on the wrong data. ### Proposal Define in the documentation a dictionary associating integers with axis of tensors. -The onnx description should specify exactly how the axis of the input tensor is splited and indicate precisely where each of the outputs in the following layers of the network are used. -## Issue #2.2: Incomplete specification of CONCAT operator - -- CAT: Operator -- CRIT: High -- REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Concat.html +## Issue #1.12: Associate dimensions of input tensors with reshaping tensors +- CAT: Format +- CRI: LOW +- REQ: (TBC) +- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html + https://onnx.ai/onnx/operators/onnx__Reshape.html + https://onnx.ai/onnx/operators/onnx__Shape.html + ### Issue -1. The "axis" attribute gives an integer which defines the axis along which the n input tensors should be concatenated. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor. In general, the "axis" attribute of the concat operator is set to 1, but which axis corresponds to 1 ? Does this mean that we must concatenate the channels of the input tensors, if "axis=1" corresponds to the channels ? -2. Moreover, the onnx description does not specify the order in which the input tensors are concatenated. +This issue is linked to the previous issue (1.11). Some operators take as input a tensor defining the dimensions of the output tensor based on the input data tensor(s). But the onnx documentation does not specify exactly the correspondance between this reshaping tensor and the axis of the input data tensor to be reshaped. +- The input named 'scales' of the RESIZE operator (in CNNs) gives a tensor which indicate the resize of each dimension of the input data tensor. Each element of 'scales' corresponds to an axis of the input data tensor. But the documentation does not specify the correspondance between the position of the element on the 'scales' tensor and the axe of the input tensor. Does the first element of the 'scales' tensor correspond to batch of the input tensor ? Does the second element of the 'scales' tensor correspond to the channels of the input tensor ? Does the third element of the 'scales' tensor correspond to the rows of the input tensor ? ... +- The input named 'shape' of the RESHAPE operator is a shape tensor which specifies the output shape. But the documentation does not specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor. Is it the value of the third element of the 'shape' tensor that determines the size of the ouput tensor rows (in case of CNNs) ? +- The SHAPE operator outputs an 1D tensor containing the shape of the input tensor. But the documentation does not specify the correspondance between the dimension of the input data tensor and the position of the element on the 'shape' output tensor. In case of CNNs, if the input was a tensor containing 16 channels of feature maps with size 80x80, what would the output tensor look like ? Would it be : 'shape' = [16,80,80] ? Or the sizes of each dimension of the input tensor should be written to the output tensor in another order ? ### Consequence -If the concatenation was done along the wrong axis of the input tensors or in the wrong order depending on the different input tensors, then the next layer of the network would give an incorrect result by applying its operation on the wrong feature maps. +- For RESIZE operator : The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted. +- For RESHAPE operator : The output tensor may be incorrect if the input tensor was reshaped based on the wrong axes. +- For SHAPE operator : The output tensor could be incorrect if the implementation of the 'shape' operator did not list the dimensions of the input tensor in the correct order. ### Proposal -Define in the documentation a dictionary associating integers with axis of tensors. -The onnx description should specify the order in which the input tensors are concatenated. +- For RESIZE operator : Specify the correspondance between the elements of the "scales" input and the axis of the input tensor to be resized. +- For RESHAPE operator : Specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor. +- For SHAPE operator : Specify in the documentation the correspondance between the dimension of the input tensor and the position of the value on the 'shape' output tensor. -## Issue #2.3: Incomplete specification of RESIZE operator +# Issues - Operators + +## Issue #2.1: Incomplete specification of SPLIT operator - CAT: Operator - CRIT: High - REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html +- LOC: https://onnx.ai/onnx/operators/onnx__Split.html ### Issue -1. The "scales" input gives a tensor which indicate the resize of each dimension. Each element of 'scales' corresponds to an axe of the input tensor. The onnx documentation indicates that the "scales" tensor takes value greater than 0. If it’s less than 1, it’s sampling down, otherwise, it’s upsampling. But the documentation does not specify the correspondance between the position of the element on the 'scales' tensor and the axe of the input tensor. Does the first element of the 'scales' tensor correspond to batch of the input tensor ? Does the second element of the 'scales' tensor correspond to the channels of the input tensor ? Does the third element of the 'scales' tensor correspond to the rows of the input tensor ? ... Moreover, if it's the value '2', we understand that the dimension of the corresponding axis is upsampling but by how much ? Does it mean that it is multiplied by 2 ? -2. The "nearest_mode" attribute indicates how to get “nearest” pixel in input tensor from x_original. There are 4 modes : "round_prefer_floor", "round_prefer_ceil", "floor", "ceil" but for no mode the documentation explains which operation applies to the tensor. What difference applies depending on the mode? +The input named "split" gives a tensor which indicate the length of the 'axis' specified for each output tensor. For example, if the shape of the input tensor is [1,32,320,320] and the "split" input is [16,16] with the "axis" attribute = 1, then the operator splits the input tensor into two output tensors [1,16,320,320] and [1,16,320,320]. These two output tensors are each an input tensor of a following layer of the CNN. However, the onnx documentation does not specify exactly which are the 16 feature maps of the input tensor which go into the first output and which are the 16 which generate the other output. ### Consequence -The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted. +The next layers of the neural network could receive the wrong feature maps and not those expected from the correct channels. ### Proposal -1. Specify the correspondance between the elements of the "scales" input and the axis of the input tensor to be resized. -2. Specify exactly the transformation applied to the input tensor depending on the assigned upsampling mode. Give an example of what the output tensor looks like from an input tensor for each of the modes +The onnx description should specify exactly how the axis of the input tensor is splited by giving a correspondence between the input tensor data and the output tensors of the operator. - -## Issue #2.4: Incomplete specification of RESHAPE operator +## Issue #2.2: Incomplete specification of CONCAT operator - CAT: Operator - CRIT: High - REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Reshape.html +- LOC: https://onnx.ai/onnx/operators/onnx__Concat.html ### Issue -The "shape" input is a shape tensor which specifies the output shape. If one dimension of the new shape is -1, the value of the output dimension is inferred from the size of the input tensor and the remaining dimensions. Let's suppose an input tensor with size [1,c,l,w] and 'shape'=[1,c,-1], in this case where the shape of the output tensor is inferior to the shape of the input tensor, does it mean that we have to reorganize the matrix lxw of feature map into an unique row of size l*w in order to obtain an output tensor with the shape [1,c,l*w] ? And how are the rows of w columns organized on a single line ? Are they concatenated one after the other ? Is an order to be respected ? The onnx documentation does not specify exactly how the dimension '-1' transform the tensor to be reshaped. And vice versa, if shape of the output tensor > shape of the input tensor (input tensor's size = [1,c,L] and 'shape'=[1,c,l,w]), then how is the data from a row organized into matrices (L=l*w) ? The onnx documentation does not specify exactly how the data from the input tensor is reorganized. Moreover, the documentation does not specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor. +The onnx description does not specify the order in which the input tensors are concatenated. A list of tensors is given as input for concatenation along the attributed axis but it is not specify in which order the concatenated output tensor contains the input tensors in the specified axis. ### Consequence -1. The output tensor may be incorrect if the input tensor was reshaped based on the wrong axes. -2. The output tensor may be incorrect if the reordering data in one dimension was done in the wrong order --> the operations of the next layers would be distorted. +If the concatenation was done in the wrong order depending on the different input tensors, then the next layer of the network would give an incorrect result by applying its operation on the wrong feature maps. ### Proposal -1. Specify the correspondance between the position of the element on the 'shape' tensor and the axe of the input tensor. -2. Specify exactly how the dimension '-1' transform the tensor to be reshaped. -3. Specify exactly how the data from the tensor is reorganized. - +The onnx description should specify the order in which the input tensors are concatenated (by giving a number to each input tensor ?). -## Issue #2.5: Incomplete specification of SOFTMAX operator +## Issue #2.3: Incomplete specification of RESIZE operator - CAT: Operator - CRIT: High - REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Softmax.html +- LOC: https://onnx.ai/onnx/operators/onnx__Resize.html ### Issue -Softmax(input, axis) = Exp(input) / ReduceSum(Exp(input), axis=axis, keepdims=1) -The “axis” attribute indicates the dimension along which Softmax will be performed. But the onnx documentation does not specify the correspondance between the integer given to the "axis" attribute and the axe of the input tensor. If 'axis=1', does it mean that Softmax is performed along the channels of the input tensor ? +1. The input named "scales" gives a tensor which indicate the resize of each dimension. Each element of 'scales' corresponds to an axe of the input tensor. The onnx documentation indicates that the "scales" tensor takes value greater than 0. If it’s less than 1, it’s sampling down, otherwise, it’s upsampling. If it's the value '2', we understand that the dimension of the corresponding axis is upsampling but by how much ? Does it mean that it is multiplied by 2 ? +2. The "nearest_mode" attribute indicates how to get “nearest” pixel in input tensor from x_original. There are 4 modes : "round_prefer_floor", "round_prefer_ceil", "floor", "ceil" but for no mode the documentation explains which operation applies to the tensor. What difference applies depending on the mode ? ### Consequence -The values of elements in the feature maps of the output tensor could be incorrect if the softmax was performed along the wrong axe of the input tensor. +The ouput tensor could be the incorrect shape with incorrect elements in the feature maps if the rescaling of the dimension was misunderstood. The operations of the next layers would be distorted. ### Proposal -Specify in the documentation the correspondance between the integer given to the "axis" attribute and the axe of the input tensor. - +Specify exactly the transformation applied to the input tensor depending on the assigned upsampling mode. Give an example of what the output tensor looks like from an input tensor for each of the modes -## Issue #2.6 : Incomplete specification of SHAPE operator +## Issue #2.4: Incomplete specification of RESHAPE operator - CAT: Operator - CRIT: High - REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Shape.html +- LOC: https://onnx.ai/onnx/operators/onnx__Reshape.html ### Issue -The "shape" operator outputs an 1D tensor containing the shape of the input tensor. But the onnx documentation does not specify the correspondance between the dimension of the input tensor and the position of the element on the 'shape' output tensor. If the input was a tensor containing 16 channels of feature maps with size 80x80, what would the output tensor look like ? 'shape' = [16,80,80] ? Or the sizes of each dimension of the input tensor should be written to the output tensor in another order ? +The input named "shape" is a tensor which specifies the output shape. If one dimension of the new shape is -1, the value of the output dimension is inferred from the size of the input tensor and the remaining dimensions. Let's suppose an input tensor with size [1,c,l,w] and 'shape'=[1,c,-1], in this case where the shape of the output tensor is inferior to the shape of the input tensor, does it mean that we have to reorganize the matrix lxw of feature map into an unique row of size l*w in order to obtain an output tensor with the shape [1,c,l*w] ? And how are the rows of w columns organized on a single line ? Are they concatenated one after the other ? Is an order to be respected ? The onnx documentation does not specify exactly how the dimension '-1' transform the tensor to be reshaped. And vice versa, if shape of the output tensor > shape of the input tensor (input tensor's size = [1,c,L] and 'shape'=[1,c,l,w]), then how is the data from a row organized into matrices (L=l*w) ? The onnx documentation does not specify exactly how the data from the input tensor is reorganized. ### Consequence -The output tensor could be incorrect if the implementation of the 'shape' operator did not list the dimensions of the input tensor in the correct order. +The output tensor may be incorrect if the reordering data in one dimension was done in the wrong order --> the operations of the next layers would be distorted. ### Proposal -1. Specify in the documentation the correspondance between the dimension of the input tensor and the position of the value on the 'shape' output tensor. -2. Note in the onnx file of description of the neural network the shape of the layer input as well as the layer output. +1. Specify exactly how the dimension '-1' transform the tensor to be reshaped. +2. Specify exactly how the data from the input tensor is reorganized. - -## Issue #2.7 : Incomplete specification of SLICE operator - -- CAT: Operator -- CRIT: High -- REQ: (TBC) -- LOC: https://onnx.ai/onnx/operators/onnx__Slice.html +## Issue #2.5: incomplete specification of CONV operator +- CAT: Operator +- CRI: HIGH +- REQ: (TBC) +- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__Conv.html), but this issue appear in other operators ### Issue -The "axes" attribute gives an integer which defines the axis along which the input tensor should be sliced. The onnx documentation does not specify the correspondance between the integer and the axis of the tensor. +- The description of the CONV operator is very abstract: "The convolution operator consumes an input tensor and a filter, and computes the output.". +- The value of the padding is not defined (it is actually 0). +- Presentation of attributes makes it difficult to check if all dependencies are expressed. ### Consequence -When implementing a neural network containing a "slice" operator, the slice operation on an input tensor can be performed on the wrong axis et so it would give an incorrect result. +- Implementer needs to check the referece implementation (or other doc.) to understand what needs to be implemented. Different implementations may lead to different results. ### Proposal -Define in the documentation a dictionary associating integers with axis of tensors. - +See [example](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/documents/conv_specification_example) -## Issue #2.8: incomplete specification of CONV operator +## Issue #2.6: Batches - CAT: Operator - CRI: HIGH - REQ: (TBC) -- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__Conv.html), but this issue appear in other operators +- LOC: [CONV operator](https://onnx.ai/onnx/operators/onnx__MatMul.html), +- AUT: Nicolas + +In the ONNX tensor semantics, there is no notion of a batch dimension. -### Issue -The description of the CONV operator is very abstract: "The convolution operator consumes an input tensor and a filter, and computes the output.". +In principle, inconsistencies are detected at runtime through shape inference when convolutions are involved, because it is convolutions that introduce batch semantics, which then propagate to the preceding/following operators. -The value of the padding is not defined (it is actually 0). +However, if we consider a network without convolutions, there is no longer any batch semantics. -Presentation of attributes makes it difficult to check if all dependencies are expressed. +So, if we take a MatMul that receives an input tensor with T.shape = [2,3], it's unclear whether this is a 1D batch of size 2, or a 2D tensor with no batch dimension. -### Consequence -Implementer needs to check the referece implementation (or other doc.) to understand what needs to be implemented. Different implementation may lead to different results. +The ONNX specification for MatMul refers to NumPy: +> If both arguments are 2-D they are multiplied like conventional matrices. +> If either argument is N-D, N > 2, it is treated as a stack of matrices residing in the last two indexes and broadcast accordingly. -### Proposal -See [example](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/documents/conv_specification_example) +Some frameworks (such as https://github.com/Verified-Intelligence/auto_LiRPA) assume that the first dimension is always the batch, but without stating it explicitly — which can lead to hours of debugging to figure out. In this case, in my opinion, it is a misinterpretation (bug) in the framework. + +Since we more or less agreed to be as explicit as possible in SONNX, +I suggest that for MatMul and Gemm we explain the following: +- [3] is a 1D tensor of dimension 3 +- [1,3] is a 2D tensor of dimensions 1 and 3 +- [1,1,3] is a batch of 1 tensor, each of shape [1,3] +=> There is no way to specify a 1D tensor with batch size > 1 +Workaround: [2,1,3] is a batch of 2 tensors, each of shape [1,3] diff --git a/safety-related-profile/deliverables/reqs/reqs.md b/safety-related-profile/deliverables/reqs/reqs.md index 200209a9..cf20ade9 100644 --- a/safety-related-profile/deliverables/reqs/reqs.md +++ b/safety-related-profile/deliverables/reqs/reqs.md @@ -3,462 +3,472 @@ This document captures the requirements applicable to the SONNX profile. The specification is organized as follows: - - General requirements - Requirements about the operators - Requirements about the graph - Requirement about the serialization format -# Operators - +## Lexicon +- **inference**:The phase where the model applies what it has learned to make predictions on new, unseen data, without updating its parameters. +- **training**: The phase where the model learns from data by adjusting its internal parameters to minimize error. + ## General requirements -### REQ `OP 000`: `Compliance with the ONNX standard` - -#### Description -The SONNX standard shall not modify the structure (inputs, outputs, attributes) of the ONNX operators ). However, it is allowed to restrict the input and parameter domains if deemed necessary. - -#### Rationale -Compatibility with the ONNX standard. - -#### Related need -[TBC] - -### REQ `OP 001`: `Operators set` +### Documentation -#### Description -The SONNX profile shall include the following operators: -> [TBC]: list to be established from the [list of use cases](./usecases.md). See also [Excel file](../meetings/reqs_sub_wg/ONNX_operators_for_Use_Cases.xlsx) - -#### Rationale -The set of operators included in the SONNX profile shall allow the implementation of simple industral use cases by the end of 2025. +The documentation of the SONNX profile is composed of two parts: +- a documentary part, used to document the operator semantics +- a formal part, used to specify the exact behaviour of the operator. -#### Related need -[TBC] +The documentary part shall not be considered to be a formal specification. For an exact and precise specification of an operator, the user shall refer to the formal specification. -### REQ `OP 002`: `I/O specification` +### REQ-DO-010: Documentation structure #### Description -For each operator in the SONNX operator set, the SONNX profile shall completely and precisely determine the expected values of the operator's outputs for any valid value of its inputs and attributes.\ -A *valid* input (resp. attribute) is an input (resp. attribute) whose value comply with all preconditions (see [REQ XXX](#XXXX)).\ -Depending on the operator, the expected value may be unique or not.\ -In the latter case, the expected value may be a range of values, or a set of values satisfying some properties (post-condition). - -The expected value will preferably be defined by a mathematical relation between inputs, outputs, and attributes. -Usual mathematical notations (i.e., notations that are commonly used by engineers) must be used preferably. +The SONNX profile shall describe each operator according to the following structure: +- a summary of the restrictions applicable to the operator +- the signature of the operator +- the inputs, attributes and output of the operator +- a documentation about the semantics of the operator, which may include illustrations, code samples, etc. +- a formal specification of the operator semantics written in Why3 #### Rationale -There shall be no room for interpretation or non determinism. +Homogeneity, readability. #### Related need [TBC] - -### REQ `OP 003`: `Accuracy` +### REQ-DO-020: Notation consistency #### Description -The specification shall provide -- the maximal acceptable error (upper bound) on the outputs -- or the inputs leading to the maximal error (the measurement of errors being left to the implementer). - -The error may be expressed for each scalar or, on the matrice, vector, or tensor. In any case, the error metric must be clearly defined. +The SONNX profile shall use consistent notations for the description of all operators. #### Rationale -If we say nothing about the accuracy of an operator, it basically mean that any output is correct. +Homogeneity, readability. + +#### Notes +SONNX may provide a set of standard notations to be used in the documentation. -Note: this can be considered as a specific case of requirement [req-op-002](#req-op-002-io-specification) about the operator's I/O relation. +For instance, the following conventions may be applied: +- Tensors are represented in uppercase (e.g., `X`,`B`,...) +- Attributes are represented in lowercase (`auto_pad`, `group`,...) +- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$ +- The number of channels of a tensor `T` is denoted by $nch(T)$. #### Related need [TBC] -## Informal specification - -### REQ `OP 010`: `Informal specification - structure` +### REQ-DO-030: Naming consistency #### Description -The SONNX profile shall describe each operator according to the same structure including: -- The operator's inputs and outputs -- The operator's attributes -- The semantics of the operator using a mathematical notation possibly illustrated by figures when applicable. -- Any intermediate variable introduced for the sake of the specification must be defined explicitly. - -The description shall follow the example given for the `conv` operator. +The SONNX profile shall use consistent terms for the description of all operators. #### Rationale -Homogeneity, readability. +Consistency, readability. + +#### Notes +SONNX may provide a set of standard terms to be used in the documentation. + +For instance: +- "kernel" (not "filter"), as in "convolution *kernel*" +- "spatial dimensions" (not "spatial *axes*") +- etc. #### Related need [TBC] -### REQ `OP 011`: `Consistency of notations` +### RECO-DO-001: Simplicity #### Description -The SONNX profile shall use consistent notations for the description of all operators. - -The following conventions apply: -- Tensors are represented in uppercase (e.g., `X`,`B`,...) -- Attributes are represented in lowercase (`auto_pad`, `group`,...) -- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$ -- The number of channels of a tensor `T` is denoted by $nch(T)$. +In the documentation part, the SONNX profile should describe the operator semantics in the simplest and most intuitive way.\ +It shall be as close as possible to the standard mathematical description of the operator, without optimization (those are left to the implementer). #### Rationale -Homogeneity, readability. +The documentation shall be easy to understand and shall facilitate validation and verification activities. #### Related need [TBC] -### REQ `OP 012`: `Consistency of naming` +### REQ-DO-050: No implementation prescription #### Description -The SONNX profile shall use consistent terms for the description of all operators: -- "kernel" (not "filter"), as in "convolution *kernel*" -- "spatial dimensions" (not "spatial *axes*") - -*This list is to be completed.* +The SONNX profile shall not mandate specific implementation solutions. However, if a particular implementation is to be preferred in practice, the specification shall define the properties that such an implementation satisfies. #### Rationale -Consistency, radability. +SONNX is a specification and shall not prescribe implementation. Should the model designer need to impose a specific implementation solution, this information will be expressed using specific meta-data (see [derived requirements](#derived_reqs)). + +#### Notes +The relation between inputs and outputs may also be expressed by algorithm describing +how inputs are processed to generate produce the outputs according to the attributes. +In that case, the algorithm shall not be considered as a requirement on the implementation, +but only as one possible way to compute the expected result. #### Related need [TBC] -### REQ `OP 013`: `Simplicity` +### RECO-DO-001: Conditions stated once #### Description -The SONNX profile shall describe the operator semantics in the simplest and most intuitive way.\ -In an algorithmic description is used, it shall be as straightforward as possibe with no -fancy optimization (that are left to the implementer). +The SONNX profile should ensure that, when a condition involves several inputs, outputs or attributes, it is only expressed once in the section dedicated to one of the inputs, outputs or attributes. Should the condition involve multiple inputs, outputs or attributes, references (hyperlinks) to the unique condition shall be used in all other sections. -#### Rationale -The specification shall be easy to understand and shall facilitate validation and verification activities. +#### Rationale +For instance, if two inputs `A` and `B` are constrained by some relation C, this relation must appear in the section concerning `A` *and* in the section concerning `B`. Duplicating the constraint raises a risk of inconsistency. #### Related need [TBC] -### REQ `OP 014`: `No implementation prescription` +## Formal specification + +### REQ-FS-000: Formal specification #### Description -The SONNX profile shall not prescribe implementation solutions.\ -The relation between inputs and outputs may also be expressed by algorithm describing -how inputs are processed to generate produce the outputs according to the attributes. -In that case, the algorithm shall not be considered as a requirement on the implementation, -but only as one possible way to compute the expected result. +The SONNX profile shall provide a formal specification of each operator. -#### Rationale -SONX is a specification and shall not prescribe implementation. Should the model designer need to -impose a specific implementation solution, this information shall be expressed in a specific meta-data (see [derived requirements](#derived_reqs)). +#### Rationale +The formal specification uses a formal language with a well-defined and sound semantics that shows none of the potential ambiguities of the informal specification operators used in the documentary part. In particular, the formal specification can be used to prove the correctness of an implementation or be used as a test oracle. #### Related need [TBC] -### REQ `OP 015`: `Restrictions wrt ONNX` +### REQ-FS-010: Formal specification traceability #### Description -The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction -with respect to the ONNX standard. +The formal specification must be traceable to the documentation, i.e., the elements of the formal specification shall refer to the sections and or paragraphs of the documentary part. -In that case, the condition must be maked with tag `[restrict]`. - -#### Rationale -Clarity. +#### Rationale +The formal specification of an operator perfectly defines it, but the semantics so expressed may not reflect the operator designer intent. Having an explicit link between the formal specification and its informal counterpart is one way (i) to prevent errors (both specifications are redundant to some extent) and (ii) to enforce the formal specification to remain "simple". #### Related need [TBC] -### REQ `OP 016`: `No default values` +### REQ-FO-020: Operator versions #### Description -The SONNX profile shall forbid the use of defaut values. +The model shall indicate precisely the version of each operator used in the model. #### Rationale -The ONNX standard defines default value for attributes that are left without values. - -The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations. +Operators may have several versions (opset) and, during graph execution, the shall be no ambiguity about the version to be used. #### Related need -[TBC] +[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model) -### REQ `OP 017`: `Datatypes` +### REQ-FO-030: Representation of parameters #### Description -The SONNX standard shall -- specify operators for values in the domain of real numbers ($R$), systematically -- specify operators for all domain necesary to support the industrrial use cases (e.g, `float32`and `int32`). - -For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes. +The model shall shall specify all parameters down to the least significant bit and in a non-ambiguous way. For instance, the IEEE hexadecimal binary representation may be used to represent floating point parameters ([-]0x1.abcdefp[+-]n) . #### Rationale -The semantics of the operator may depend on the types (float, integers), accurracy (float32, float64), range (int16, int32)of numbers. +The serialization of floating point number must not degrade the accuracy of the source model parameters. #### Related need [TBC] -### REQ `OP 018`: `Input domain definition` +### REQ-FO-040: Documentation of input and output tensors #### Description -The SONNX profile shall specify all conditions on inputs, outputs, and attributes that must be satisfied for the operator to be applicable. The behaviour of the operator for any value out of the valid input domain must be described. +The SONNX file format shall have the capability to describe +- the semantics of the input and output tensors, +- the semantics of the dimensions of the tensors. #### Rationale -The semantics of operators is only defined in the inputs, outputs, and attribute validity domain. +[TBC] #### Related need -[TBC] +[need-arcys-001](needs.md#need-arcys-001-final-prediction) -### REQ `OP 019`: `Behaviour in case of errors` +### REQ-FO-050: Derived requirements - implementation #### Description -The SONNX profile shall specify the expected behaviour of an operator should -- the input values be out of the valid input domain -- over/under flow occur +The SONNX profile shall have the capability to attach meta-data on model elements. Those meta-data may be used to describe e.g., +- the exact order in which the graph operators must be executed +- the target hardware on which the model or part of the model must be deployed +- the semantics of tensors +- etc. #### Rationale [TBC] #### Related need -[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) +[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements) + -### REQ `OP 020`: `Overflow and underflow` +# Operators + +## Operator set + +### REQ-OP-010: Deterministic operators #### Description -The SONNX profile shall specify the conditions leading to overflows and underflows. +The profile shall only include functional operators, or restriction shall be expressed to ensure that the operator will behave as a function during inference. + +A functional operator is an operator that maps one input value to one and only one output value. For instance, the ONNX ``dropout`` operator with ``training_mode`` set to ``true`` performs a random dropout that is not functional. A functional behaviour may be achieved by restricting the use of the operator, for instance, by enforcing input ``training_mode`` to be set to false. Operator ``RandomUniform`` is another example: if the seed input is set to some fixed value, the operator becomes functional. Checking that the input value may be done by a dedicated tool. #### Rationale -[TBC] +A graph must be deterministic. #### Related need [need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) +### REQ OP 000: Operator set + +#### Description +The SONNX profile shall include at least the following operators: + + +| Operator | +|------------------------------| +| Abs | +| Add | +| Cast | +| Clip | +| Concat | +| Constant | +| ConstantOfShape | +| Conv | +| ConvTranspose | +| Dense | +| Div | +| Equal | +| Erf | +| Exp | +| Expand | +| Flatten | +| FullyConnected | +| Gather | +| Gemm | +| GlobalAveragePool | +| GRU | +| HardSwish | +| Identity | +| LeakyRelu | +| Less | +| Log | +| LSTM | +| MatMul | +| Max | +| MaxPool | +| Min | +| Mod | +| Mul | +| Neg | +| Not | +| Pad | +| Padding | +| Pow | +| Range | +| ReduceMean | +| ReduceSum | +| Relu | +| Reshape | +| Resize | +| ScatterND | +| Shape | +| Sigmoid | +| Slice | +| Softmax | +| SoftPlus | +| Split | +| Sqrt | +| Squeeze | +| Sub | +| Tanh | +| Transpose | +| ConvTransposeDeconvolution | +| Unsqueeze | +| Where | -### REQ `OP 021`: `Unique conditions` -#### Description -The SONNX profile shall ensure that, when a condition involves several inputs, outputs or attributes, -the condition is only expressed once in the section dedicate to one of the inputs, outputs or attributes. -Should the condition involve multiple inputs, outputs or attributes, references to the unique condition -shall be ued in all other sections. +#### Rationale +The SONNX profile does not cover the complete set of ONNX operators. It is limited to operators (i) used during inference, (ii) that do not undermine determinism and predictability, (iii) used in a first set of industrial [use cases](../../scope/scope.md). This set may be later extended depending on the needs. -#### Rationale -Prevention of inconsistencies. #### Related need [TBC] -### REQ `OP 022`: `Stability of operators` +### REQ-OP-020: Inference operators #### Description -The SONNX profile shall preclude the use of instable operators or document this phenomenon if it occurs. +The profile shall only include operators useful during inference. For instance, `SoftmaxCrossEntropyLoss`, `NegativeLogLikelihoodLoss`or gradient operators such as `AddGrad` are forbidden. -#### Rationale -[TBC] +#### Rationale +Operators only used during training will not contribute to the computation of the graph output and represent "dead operators". #### Related need -[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) - - -### REQ `OP 023`: `Determinism of resource usage` - -#### Description -The SONNX profile shall preclude the use operators which memory usage varies dynamically (i.e, depends on the inputs). - -#### Rationale [TBC] #### Related need [need-ai-009](needs.md#need-ai-009-resource-usage-determinism-and-predictability) -### REQ `OP 024`: `Determinism of execution times` +### REQ-OP-030: Compliance with the ONNX standard #### Description -The SONNX profile shall preclude the use operators which memory usage varies dynamically (i.e, depends on the inputs). - -#### Rationale -[TBC] - -#### Related need -[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability) - - -## Formal specification +The SONNX version of an operator `op` shall have the same inputs, outputs, and attributes than the ONNX `op` operator. -### REQ `OP 030`: `Formal specification in ACSL` +However, it is allowed to restrict the inputs and parameters value domains if deemed necessary. -#### Description -The SONNX profile shall provide an ACSL specification for each operator. -#### Rationale -The formal specification will be used to verify the correctness of the reference implementation. +#### Rationale +Compatibility with the ONNX standard. #### Related need [TBC] -### REQ `OP 031`: `Generic properties` +### REQ-OP-060: Restrictions wrt ONNX #### Description -"As far as possible", the formal specification must also include high-level mathematical properties that the operator must satify (e.g., symmetry, reflexivity, existence of a neutral element, etc.). For instance, `sub(T,T)=0`, `sub(T,0)=T`,`transpose(tranpose(T))=T`... - - Whenever possible, the formal specification may rely on other operator, e.g., `sub(add(T1,T2), T2)=T1`, . This schema may be applied on more complex operators such as convolution. For instance: `ConvTranspose(Conv(X))=X and `Conv(Conv`Transpose(X))=X" for specific values of attributes. +The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction with respect to the ONNX standard. -See example of the `conv` operator. +In that case, the condition must be marked with tag `[restrict]`. -#### Rationale -If an operator is described "algorithmically", it will be very close to the actual (e.g.) C implementation. Additional properties may be useful to detect errors in the formal specification. +#### Rationale +Clarity. #### Related need [TBC] -## Profile contents -### REQ `OP 032`: `Deterministic operators` +## Operator specification + +### REQ-OP-030: Input domain specification #### Description -The profile shall only contain deterministic operators. The profile shall forbib any operator whose behaviour is intrinsically non-deterministic or for which some inputs determining the behaviour of the operators would not be identified (e.g., an operator that would use a random generator with no control on the random generator seed). +For each operator in the SONNX operator set, the SONNX profile shall specify the validity domain of inputs and attributes. #### Rationale -[TBC] - +There shall be no room for interpretation or non determinism. #### Related need [TBC] +### REQ-OP-030: Operator specification -# Requirements on graph interpretation +#### Description +For each operator in the SONNX operator set, the SONNX profile shall specify the expected output values for any input values and attributes in their validity domain. -### REQ `GR 000`: `Graph interpretation` +The SONNX standard shall specify operators for values in the domain of real numbers ($R$) and in all domains necessary to support the industrial use cases (e.g, `float32`and `int32`). -#### Description -The profile shall specify how graphs are interpreted. +For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes. #### Rationale +The semantics of the operator may depend on the types (float, integers), accuracy (float32, float64) and range (int16, int32) of numbers. #### Related need [TBC] -# Requirements on file format - -## General requirements - -### REQ `FO 000`: `Operator versions`` - -#### Description -The model shall indicate precisely the version of each operator used in the model. There shal be no ambiguity about the version to be used. #### Rationale -Operators may have several versions (opset) +There shall be no room for interpretation or non determinism. #### Related need -[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model) +[TBC] -### REQ `FO 001`: `Representation of parameters` +### REQ-OP-040: Out of domain errors #### Description -The representation of parameters (weights, biases) in the serialized representation of the model shall not degrade the accuracy of parameters. For instance, the IEEE hexadecimal binary representation ([-]0x1.abcdefp[+-]n) may be used to represent floating point parameters. +The SONNX profile shall specify the behaviour of an operator should some input value be out of its validity domain. #### Rationale -The serialization of floating point number must not introduce degrade the accuracy of the source model parameters. +Out of domain conditions may not be prevented by design, checked statically or detected at runtime. +For attributes, a static verification of the model is expected to be carried out in order to prevent such error conditions. #### Related need -[TBC] - -## User documentation +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) -### REQ `FO 010`: `Documentation of input and output tensors` +### REQ-OP-050: Overflow and underflow conditions #### Description -The SONNX profile must provide the capability to give the semantics of the input and ouptut tensors, including the semantics of the dimensions of the tensors. +The SONNX profile shall specify the conditions leading to overflows, underflows, wrap-around. #### Rationale [TBC] #### Related need -[need-arcys-001](needs.md#need-arcys-001-final-prediction) +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) -### REQ `FO 011`: `Derived requirements - implementatiobn` -#### Description -The SONNX profile shall provide capability (metadata) to describe how the model must be deployed on a specific target, i.e., -- the exact order in which the graph operators must be executed -- the target hardware on which the model or part of the model must be deployed. +### REQ-OP-070: No default value -> To be discussed. Shall it be part of the MLMD or be part of another document? -> What kind of derived requirements do we want to express? + #### Description +The SONNX profile shall forbid the use of default values. #### Rationale -[TBC] +The ONNX standard defines default value for attributes that are left without values. The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations. #### Related need -[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements) +[TBC] + +# Requirements on graph -### REQ `FO 012`: `Traceability to training model` +### REQ-GR-000: Graph specification #### Description -The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated. +The profile shall specify the graph execution semantics. #### Rationale -[TBC] +A model is a graph of operators. The semantics of the graph defines how operators are applied to generate the graph outputs out of its inputs. #### Related need -[need-ai-004](needs.md#need-ai-004-support-for-traceability) - +[TBC] -### REQ `FO 013`: `Traceability to training environment` +### REQ-GR-000: Explicit types and shapes #### Description -The SONNX profile must provide the capability to trace the environment used for training. +All numerical types must be indicated explicitly (no type inference).\ +All shape conversion must be done explicitly (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html)) #### Rationale +[TBC] #### Related need -[need-ai-004](needs.md#need-ai-004-support-for-traceability) - +[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators) -# Model validity +# Requirements on file format -### REQ `VA 000`: `Valid operator set` +### REQ-FO-000: Compatibility with ONNX #### Description -A model shall only use operators in the SONNX set. +Any model compliant with the SONNX file format shall be a correct ONNX model. #### Rationale -[TBC] +Compliance with ONNX. #### Related need [TBC] -### REQ `VA 001`: `Explicit types and shapes` +### REQ-FO-010: Format completeness #### Description -All datatypes must be indicated explicity (no type inference).\ -All shape conversion must be peformed explicity (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html)) +The SONNX description of a model shall fully specify the function computed by the model. #### Rationale -[TBC] +A SONNX model shall leave no room to interpretation. #### Related need -[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators) - +[TBC] # Reference implementation -### REQ `RI 000`: `Reference implementation` +### REQ-RI-000: Reference implementation #### Description The SONNX profile shall provide a reference implementation covering -- the SONNX operator set +- the operator set - the graph execution -- the graph import (deszerialization of a SONNX model) +- the graph import (deserialization of a SONNX model) #### Rationale -In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we nned to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification. +In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we need to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification. #### Related need [need-ai-011](needs.md#need-ai-011-support-for-verification-activities) -### REQ `RI 001`: `Relation with specification` +### RECO-RI-010: Relation with specification #### Description -The relation between the specification and the implementation must be as straightforward as possible. In particular, the reference implementation must reproduce the structure of the mathematical specification, without introducing implementation optimizations. See example of the `CONV2D`` operator. +The reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations). + #### Rationale Verifying the reference implementation must be as easy as possible. @@ -468,18 +478,85 @@ Verifying the reference implementation must be as easy as possible. # Tooling -### REQ `TO 000`: `Verification tool` +### REQ-TO-000: Verification tool #### Description -The SONNX profile shall come with a model verification tool.\ +The SONNX profile shall provide a model verification tool.\ This tool aims at verifying that all validity conditions are satisfied, including: -- the graph structure is well-formed, -- all required metadata are present -- operators are allowed -- all conditions involving operators inputs, outputs and parameters are satisfied +- The model complies with the serialization format +- the graph described by the model is acyclic, +- the required metadata are present +- all operators used in the model belong to the SONNX profile +- conditions that can be checked statically are satisfied +- etc. + +#### Rationale +Ensure compliance of the of a model to the SONNX profile. + +#### Related need +[need-ai-007](needs.md#need-ai-007-support-for-model-verification) + + +# TO BE DISCUSSED + + +### REQ-OP-022: Stability of operators + +#### Description +The SONNX profile shall not include numerically unstable operators. If such an unstable operator is required, the instability phenomenon shall be fully characterized + +#### Rationale +[TBC] + +#### Related need +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) + + +# TO BE SUPPRESSED + +### REQ-OP-023: Determinism of resource usage + +#### Description +The SONNX profile shall only include operators which memory usage does not depend on input values. + +> To be suppressed: in most cases, this requirement concerns the implementation of the operator, not the operator itself. There might be some exceptions (operators whose semantics requires some kind of dynamic memory allocation). Check if such operators exist before adding this req. because it might lead to endless discussions... + +#### Rationale +[TBC] + +### REQ-OP-030: Determinism of execution times + +#### Description +The SONNX profile shall not include operators which execution time depend on input values. #### Rationale [TBC] #### Related need -[need-ai-007](needs.md#need-ai-007-support-for-model-verification) \ No newline at end of file +[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability) + +### REQ-FO-012: Traceability to training model + +[TBDis] + +#### Description +The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated. + +#### Rationale +[TBC] + +#### Related need +[need-ai-004](needs.md#need-ai-004-support-for-traceability) + +### REQ-FO-013: Traceability to training environment + +[TBDis] + +#### Description +The SONNX profile must provide the capability to trace the environment used for training. + +#### Rationale + +#### Related need +[need-ai-004](needs.md#need-ai-004-support-for-traceability) + diff --git a/safety-related-profile/deliverables/reqs/reviews/edoardo.md b/safety-related-profile/deliverables/reqs/reviews/edoardo.md new file mode 100644 index 00000000..e78f9025 --- /dev/null +++ b/safety-related-profile/deliverables/reqs/reviews/edoardo.md @@ -0,0 +1,644 @@ +# Introduction + +This document captures the requirements applicable to the SONNX profile. + +The specification is organized as follows: + +- General requirements +- Requirements about the operators +- Requirements about the graph +- Requirement about the serialization format + +> **_NOTE:_** General requirements are presented later in the document. Move them here at the beginning. Perhaps create also a list by collating general operator, graph and serialization-specific requirements? +> OK: moved. + +## General requirements + +> **_NOTE:_** Move the general requirements section to the top. It will act as a good introduction to the requirements. \ +> OK: Done + +### Documentation + +The documentation of the SONNX profile is composed of two parts: +- a documentary part, used to document the operator semantics +- a formal part, used to specify the exact behaviour of the operator. + +The documentary part shall not be considered to be a formal specification. For an exact and precise specification of an operator, the user shall refer to the formal specification. + +> **_NOTE:_** This section is a bit "meta". Shall we move it to the top of the document? It would work great under "general requirements".\ +> OK: moved + +### REQ-DO-010: Documentation structure + +#### Description +The SONNX profile shall describe each operator according to the following structure: +- a summary of the restrictions applicable to the operator +- the signature of the operator +- the inputs, attributes and output of the operator +- a documentation about the semantics of the operator, which may include illustrations, code samples, etc. +- a formal specification of the operator semantics written in Why3 + +#### Rationale +Homogeneity, readability. + +#### Related need +[TBC] + +### REQ-DO-020: Notation consistency + +#### Description +The SONNX profile shall use consistent notations for the description of all operators. + +#### Rationale +Homogeneity, readability. + +#### Notes +SONNX may provide a set of standard notations to be used in the documentation. + +For instance, the following conventions may be applied: +- Tensors are represented in uppercase (e.g., `X`,`B`,...) +- Attributes are represented in lowercase (`auto_pad`, `group`,...) +- The number of lines and columns of a 2-dimension tensor `T` are respectively denoted by $nl(T)$ and $nc(T)$ +- The number of channels of a tensor `T` is denoted by $nch(T)$. + +#### Related need +[TBC] + +### REQ-DO-030: Naming consistency + +#### Description +The SONNX profile shall use consistent terms for the description of all operators. + +#### Rationale +Consistency, readability. + +#### Notes +SONNX may provide a set of standard terms to be used in the documentation. + +For instance: +- "kernel" (not "filter"), as in "convolution *kernel*" +- "spatial dimensions" (not "spatial *axes*") +- etc. + +#### Related need +[TBC] + +### REQ-DO-040: Simplicity + +#### Description +In the documentation part, the SONNX profile shall describe the operator semantics in the simplest and most intuitive way.\ +It shall be as close as possible to the standard mathematical description of the operator, without optimization (those are left to the implementer). + +> **_NOTE:_** This is very important, yet very difficult to define. I can already see some future discussion about what "simplicity" means... Ideas to improve: add a reference style such as "understandable by anyone with a STEM undergraduate/anyone who can implement a neural network in PyTorch", "in a similar style of the MatLab/TensorFlow documentation", etc.\ +> ??: I fully agree with you. This is not really a "requirement", but we don need to say something about it. I don't really like the reference to "STEM undergraduate or someone who can implement...". Another way can be to define a set of basic constructs / math operations and require all specs to be build using these elements. This would not prevent specifications to be written in a convoluted way (for instance, an optimized way)... Rather than a req, this could simply be a recommendation. It will be up to the reviewer of the operator spec to determine whether or not it must be rewritten or not... +> Final: Change this "req" a "reco" + +#### Rationale +The documentation shall be easy to understand and shall facilitate validation and verification activities. + +#### Related need +[TBC] + +### REQ-DO-050: No implementation prescription + +#### Description +The SONNX profile shall not mandate specific implementation solutions. However, if a particular implementation is to be preferred in practice, the specification shall define the properties that such an implementation satisfies. + +#### Rationale +SONNX is a specification and shall not prescribe implementation. Should the model designer need to impose a specific implementation solution, this information will be expressed using specific meta-data (see [derived requirements](#derived_reqs)). + +#### Notes +The relation between inputs and outputs may also be expressed by algorithm describing +how inputs are processed to generate produce the outputs according to the attributes. +In that case, the algorithm shall not be considered as a requirement on the implementation, +but only as one possible way to compute the expected result. + +#### Related need +[TBC] + + +### REQ-DO-060: Conditions stated once + +#### Description +The SONNX profile shall ensure that, when a condition involves several inputs, outputs or attributes, it is only expressed once in the section dedicated to one of the inputs, outputs or attributes. Should the condition involve multiple inputs, outputs or attributes, references (hyperlinks) to the unique condition shall be used in all other sections. + +For instance, if the values of two scalar inputs `A` and `B` are such that `A` > `B` , there shall be one constraint C applicable to `A` stating that `A` > `B`, and one constraint applicable to `B` referencing constraint C. + + +> **_NOTE:_** I can guess what this mean (and I agree with it), but an example would help clarify.\ +> OK: Done +> => change the example in a "negative" way. + +#### Rationale +Prevention of inconsistencies. + +#### Related need +[TBC] + +## Formal specification + +### REQ-FS-000: Formal specification + +#### Description +The SONNX profile shall provide a formal specification of each operator. + + +#### Rationale +The formal specification uses a formal language with a well-defined and sound semantics that shows none of the potential ambiguities of the informal specification operators used in the documentary part. In particular, the formal specification can be used to prove the correctness of an implementation or be used as a test oracle. + +> **_NOTE:_** Potential inconsistency with the requirement that we do not enforce a specific implementation. Maybe clarify that we only enforce a number of _properties_ of the implementation (invariants?), which are listed in the formal specification.\ +> OK: I have reworded the rationale. I see no reference to the implementation... +> => OK + +#### Related need +[TBC] + +### REQ-FS-010: Formal specification traceability + +#### Description +The formal specification must be traceable to the documentation, i.e., the elements of the formal specification shall refer to the sections and or paragraphs of the documentary part. + + +> **_NOTE:_** Define exact meaning of "traceability"\ +> > OK: defined. +> => OK + +#### Rationale +The formal specification of an operator perfectly defines it, but the semantics so expressed may not reflect the operator designer intent. Having an explicit link between the formal specification and its informal counterpart is one way (i) to prevent errors (both specifications are redundant to some extent) and (ii) to enforce the formal specification to remain "simple". + +#### Related need +[TBC] + + +### REQ-FO-020: Operator versions + +#### Description +The model shall indicate precisely the version of each operator used in the model. + +#### Rationale +Operators may have several versions (opset) and, during graph execution, the shall be no ambiguity about the version to be used. + +#### Related need +[need-thav-001](needs.md#need-thav-001-version-index-in-the-onnx-model) + + +### REQ-FO-030: Representation of parameters + +#### Description +The model shall shall specify all parameters down to the least significant bit and in a non-ambiguous way. For instance, the IEEE hexadecimal binary representation may be used to represent floating point parameters ([-]0x1.abcdefp[+-]n) . + +> **_NOTE:_** Rephrase in positive: e.g. "the model shall specify all parameters down to the least significant bit and in a non-ambiguous way". I like the hexadecimal representation for IEEE754 floats.\ +> OK: done +> => OK + +#### Rationale +The serialization of floating point number must not degrade the accuracy of the source model parameters. + +#### Related need +[TBC] + +### REQ-FO-040: Documentation of input and output tensors + +#### Description +The SONNX file format shall have the capability to describe +- the semantics of the input and output tensors, +- the semantics of the dimensions of the tensors. + +#### Rationale +[TBC] + +#### Related need +[need-arcys-001](needs.md#need-arcys-001-final-prediction) + +### REQ-FO-050: Derived requirements - implementation + +#### Description +The SONNX profile shall have the capability to describe how the model must be deployed on a specific target, i.e., +- the exact order in which the graph operators must be executed +- the target hardware on which the model or part of the model must be deployed. + +> **_NOTE:_** This is ambitious. I get how it may be useful to guarantee deterministic behaviour. But "target hardware" may be split into different levels of abstraction, e.g. "any GPU" vs "that specific model of GPU running that specific version of CUDA".\ +> ??: I am really wondering if this req has to be kept... Note that the req makes no assumption about the target, it only states that there shall be a way to specify the exact order in which operations (as defined in the model) are to be computed. The main question is: is it useful? is it necessary? +> => Simply provide the capability to add meta-data in the model. Check whether they exist or not. + +#### Rationale +[TBC] + +#### Related need +[need-ai-003](needs.md#need-ai-003-expression-of-implementation-requirements) + + +# Operators + + +## Operator set + + +### REQ-OP-010: Deterministic operators + +#### Description +The profile shall only include functional operators, or restriction shall be expressed to ensure that the operator will behave as a function during inference. + +A functional operator is an operator that maps one input value to one and only one output value. For instance, the ONNX ``dropout`` operator with ``training_mode`` set to ``true`` performs a random dropout that is not functional. A functional behaviour may be achieved by restricting the use of the operator, for instance, by enforcing input ``training_mode`` to be set to false. Operator ``RandomUniform`` is another example: if the seed input is set to some fixed value, the operator becomes functional. Checking that the input value may be done by a dedicated tool. + +> **_NOTE:_** Add examples to clarify? E.g. drop-out layers. Clarify whether the requirement extends to the _implementation_ of the operator: parallel computing in floating-point is non deterministic. +> ??: I have rewritten the req in a positive way. I have added examples. I have used the term "functional" rather than "deterministic". +=> OK + +#### Rationale +A graph must be deterministic. + +#### Related need +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) + +### REQ OP 000: Operator set + +#### Description +The SONNX profile shall include at least the following operators: + + +| Operator | +|------------------------------| +| Abs | +| Add | +| Cast | +| Clip | +| Concat | +| Constant | +| ConstantOfShape | +| Conv | +| ConvTranspose | +| Dense | +| Div | +| Equal | +| Erf | +| Exp | +| Expand | +| Flatten | +| FullyConnected | +| Gather | +| Gemm | +| GlobalAveragePool | +| GRU | +| HardSwish | +| Identity | +| LeakyRelu | +| Less | +| Log | +| LSTM | +| MatMul | +| Max | +| MaxPool | +| Min | +| Mod | +| Mul | +| Neg | +| Not | +| Pad | +| Padding | +| Pow | +| Range | +| ReduceMean | +| ReduceSum | +| Relu | +| Reshape | +| Resize | +| ScatterND | +| Shape | +| Sigmoid | +| Slice | +| Softmax | +| SoftPlus | +| Split | +| Sqrt | +| Squeeze | +| Sub | +| Tanh | +| Transpose | +| ConvTransposeDeconvolution | +| Unsqueeze | +| Where | + + +#### Rationale +The SONNX profile does not cover the complete set of ONNX operators. It is limited to operators (i) used during inference, (ii) that do not undermine determinism and predictability, (iii) used in a first set of industrial [use cases](../../scope/scope.md). This set may be later extended depending on the needs. + +> **_NOTE:_** "inference", "determinism", "predictability", "use cases" should be defined before being used as a justification. +> ??: Note that this text is not part of the specification. I have introduced the requirement about determinism before. The concept of "inference" is well-known and I don't think that it deserves a definition (to be discussed). +> => Inference to be defined (lexicon to be added) + +> **_NOTE:_** We should list the use cases (with references) if possible. That will give the reader a better idea of the scope. + +#### Related need +[TBC] + + + +### REQ-OP-020: Inference operators + +#### Description +The profile shall only include operators used during inference. + +#### Rationale +SONNX is only concerned with inference. + +> **_NOTE:_** Is "inference" clear enough? Can we define it as "all numerical parameters in the graph are constant" or something like that? +> OK/ modified as proposed. +> => Modify the rationale ("dead code"). + +#### Related need +[TBC] + +#### Related need +[need-ai-009](needs.md#need-ai-009-resource-usage-determinism-and-predictability) + + +### REQ-OP-030: Compliance with the ONNX standard + +#### Description +The SONNX version of an operator `op` shall have the same inputs, outputs, and attributes than the ONNX `op` operator. + +However, it is allowed to restrict the inputs and parameters value domains if deemed necessary. + +> **_NOTE:_** CHange "ranges" to "values"?\ +> ?? : "ranges" => "domains" +> => OK + +#### Rationale +Compatibility with the ONNX standard. + +#### Related need +[TBC] + +### REQ-OP-060: Restrictions wrt ONNX + +#### Description +The SONNX profile shall clearly indicate when a condition on the inputs, outputs, and attributes is a restriction with respect to the ONNX standard. + +In that case, the condition must be marked with tag `[restrict]`. + +> **_NOTE:_** Move this requirement closer to requirement "compliance with the ONNX standard" as they are related?\ +> OK: moved +> => OK + +#### Rationale +Clarity. + +#### Related need +[TBC] + + +## Operator specification + +### REQ-OP-030: Input domain specification + +#### Description +For each operator in the SONNX operator set, the SONNX profile shall specify the validity domain of inputs and attributes. + +#### Rationale +There shall be no room for interpretation or non determinism. + +#### Related need +[TBC] + +### REQ-OP-030: Operator specification + +#### Description +For each operator in the SONNX operator set, the SONNX profile shall specify the expected output values for any input values and attributes in their validity domain. + +The SONNX standard shall specify operators for values in the domain of real numbers ($R$) and in all domains necessary to support the industrial use cases (e.g, `float32`and `int32`). + +For instance, the `conv` operator shall be specified for values in $R$, `float32` and `int32` datatypes. + +> **_NOTE:_** Again, I'm not against the use of qualifiers such as "systematically", but we should give a definition of their meaning at the beginning of the document.\ +> OK: I have simply removed "systematically that was not required here. I have also suppressed the dedicated req and merged it with the one concerning ht operator specification +> => OK. + +#### Rationale +The semantics of the operator may depend on the types (float, integers), accuracy (float32, float64) and range (int16, int32) of numbers. + +#### Related need +[TBC] + +> **_NOTE:_** Very nice! Do we want to introduce some specific language at the beginning of the document, like in legal documents? E.g. by "completely and specific" we mean... Also, there are multiple requirements named REQ-OP-030. \ +> ??: (i) I think that we can remove "completely" and "precisely" that brings no useful information > - requirements will be renumbered in a second phase. +> => OK + +#### Rationale +There shall be no room for interpretation or non determinism. + +#### Related need +[TBC] + +### REQ-OP-040: Out of domain errors + +#### Description +The SONNX profile shall specify the behaviour of an operator should some input value be out of its validity domain. + +#### Rationale +Out of domain conditions may not be prevented by design, checked statically or detected at runtime. +For attributes, a static verification of the model is expected to be carried out in order to prevent such error conditions. + +#### Related need +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) + +### REQ-OP-050: Overflow and underflow conditions + +#### Description +The SONNX profile shall specify the conditions leading to overflows, underflows, wrap-around. + +#### Rationale +[TBC] + +#### Related need +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) + + +### REQ-OP-070: No default value + + #### Description +The SONNX profile shall forbid the use of default values. + +#### Rationale +The ONNX standard defines default value for attributes that are left without values. The objective is to ensure that the model designer and model implementer has a clear knowledge of the values involved in computations. + +#### Related need +[TBC] + + + +# Requirements on graph + +### REQ-GR-000: Graph specification + +#### Description +The profile shall specify the graph execution semantics. + +#### Rationale +A model is a graph of operators. The semantics of the graph defines how operators are applied to generate the graph outputs out of its inputs. + +> **_NOTE:_** Missing: what is a node, what is an edge, is it a DAG, etc. +> => See if we put it before ops. + +#### Related need +[TBC] + +### REQ-GR-000: Explicit types and shapes + +#### Description +All numerical types must be indicated explicitly (no type inference).\ +All shape conversion must be done explicitly (using the `reshape`) operator. (no [shape inference](https://onnx.ai/onnx/api/shape_inference.html)) + +> **_NOTE:_** If we enforce consistent input/output shapes (types), then the latter requirement is redundant. We could keep it as an example? +> OK: chenged "datatypes" to numerical types (because in formal "theory" a "type" also covers the structure...) + +#### Rationale +[TBC] + +#### Related need +[need-thav-002](needs.md#need-thav-002-typing-of-data-handled-by-implementation-operators) + + +# Requirements on file format + +### REQ-FO-000: Compatibility with ONNX + +#### Description +Any model compliant with the SONNX file format shall be a correct ONNX model. + +#### Rationale +Compliance with ONNX. + +#### Related need +[TBC] + +### REQ-FO-010: Format completeness + +#### Description +The SONNX file format specification shall ensure that a model expressed in this format can be implemented without requiring any additional information. + +#### Rationale +A SONNX model must be completely determined, leaving no room to interpretation. + +> **_NOTE:_** Slight ambiguity here. We do not mandate a specific implementation, thus a single SONNX model accepts multiple implementations, i.e. there _is_ room for interpretation. Can we reformulate this in a better way? "sufficient number of properties"? +> => To be rephrased... + +#### Related need +[TBC] + + +# Reference implementation + +### REQ-RI-000: Reference implementation + +#### Description +The SONNX profile shall provide a reference implementation covering +- the operator set +- the graph execution +- the graph import (deserialization of a SONNX model) + +#### Rationale +In order to verify the correctness of his/her implementation, the user will need to compare the results computed by this implementation with some reference. Since we need to compare computation **results**, this means that the specification must be executable. One possible way could be to use an executable formal specification (e.g., `why3`). Another way consists to provide (i) a formal specification and (ii) a "reference" implementation demonstrated to comply with the formal specification. + +#### Related need +[need-ai-011](needs.md#need-ai-011-support-for-verification-activities) + +### REQ-RI-010: Relation with specification + +#### Description +The reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations). + +> **_NOTE:_** Again, I understand the spirit of the requirement -> make the implementation as "vanilla" as possible. However, I wonder whether we can say something as "the reference implementation satisfies all the properties of the SONNX specification, i.e. it is compliant to SONNX, and it is written in the most plain and clear style possible (no optimisations)". Just to stress that there are two separate requirements: (1) correctness/adherence to SONNX and (2) simple and easy-to-read style. +> => change to recommandation and use Edoardo's sentence. + +#### Rationale +Verifying the reference implementation must be as easy as possible. + +#### Related need +[TBC] + + +# Tooling + +### REQ-TO-000: Verification tool + +#### Description +The SONNX profile shall provide a model verification tool.\ +This tool aims at verifying that all validity conditions are satisfied, including: +- the graph structure is well-formed, +- all required metadata are present +- operators are allowed +- all conditions involving attributes are satisfied + +> **_NOTE:_** Do we need to add "is executable" as one of the conditions (well-formedness)? Also, the latter condition needs to be split into (1) conditions related to input/output/parameter _types_ are satisfied and (2) conditions related to input/output/parameter _values_ are satisfied. The conditions on values may not be trivial to verify at all, especially if they depend on very specific execution traces (e.g. proving an operator input is always positive is an NP-Hard problem). +> => Check if conditions that can be verified only cobncer attributes (and not values). Check also the case of dimensions... + +#### Rationale +[TBC] + +#### Related need +[need-ai-007](needs.md#need-ai-007-support-for-model-verification) + + +# TO BE DISCUSSED + +> **_NOTE:_** These are not bad ideas, but they are very ambiguous. +> => Ceck if we keep some of them as recos. + +### REQ-OP-022: Stability of operators + +#### Description +The SONNX profile shall not include numerically unstable operators. If such an unstable operator is required, the instability phenomenon shall be fully characterized + +#### Rationale +[TBC] + +#### Related need +[need-ai-008](needs.md#need-ai-008-behavioral-determinism-and-predictability) + + +### REQ-OP-023: Determinism of resource usage + +#### Description +The SONNX profile shall only include operators which memory usage does not depend on input values. + +#### Rationale +[TBC] + +### REQ-OP-030: Determinism of execution times + +#### Description +The SONNX profile shall not include operators which execution time depend on input values. + +#### Rationale +[TBC] + +#### Related need +[need-ai-010](needs.md#need-ai-010-execution-time-determinism-and-predictability) + +### REQ-FO-012: Traceability to training model + +[TBDis] + +#### Description +The SONNX profile must provide the capability to trace the ONNX model to the training model from which it has been generated. + +#### Rationale +[TBC] + +#### Related need +[need-ai-004](needs.md#need-ai-004-support-for-traceability) + + +### REQ-FO-013: Traceability to training environment + +[TBDis] + +#### Description +The SONNX profile must provide the capability to trace the environment used for training. + +#### Rationale + +#### Related need +[need-ai-004](needs.md#need-ai-004-support-for-traceability) + diff --git a/safety-related-profile/deliverables/scope/scope.md b/safety-related-profile/deliverables/scope/scope.md index 8d5eaaa2..28374ef1 100644 --- a/safety-related-profile/deliverables/scope/scope.md +++ b/safety-related-profile/deliverables/scope/scope.md @@ -130,28 +130,28 @@ YoloNAS ## Operators ### Priority 1 list: -1) Conv2D (no grouping nor depthwise) -2) ConvTranspose/Deconvolution +1) Conv (no grouping nor depthwise) +2) ConvInteger 3) MaxPool -4) ReLU -5) Add -6) Mul -7) Concat +4) Resize +5) QuantizeLinear +6) DequantizeLinear +7) Add +8) Mul +9) Relu +10) Concat ### Priority 2 list: -8) FullyConnected -9) Conv2D (grouping & depthwise) -10) Sub -11) Abs -12) ReduceSum -13) Transpose -14) Split -15) Slice -16) Gather -17) Squeeze -18) Unsqueeze -19) Reshape -20) Flatten +11) Conv (grouping & depthwise) +12) Gemm +13) MatMul +14) Abs +15) Sub +16) ReduceSum +17) Gelu +18) LeakyRelu +19) LayerNormalization +20) SoftMax # Use case `Helicopters` - `Valot Nicolas` diff --git a/safety-related-profile/documents/Attic/call.md b/safety-related-profile/documents/Attic/call.md new file mode 100644 index 00000000..da3e94c8 --- /dev/null +++ b/safety-related-profile/documents/Attic/call.md @@ -0,0 +1,34 @@ +Our work on defining ONNX is progressing—albeit slowly—thanks to the dedication of a **small group** of contributors. + +Initially, our meetings had around 15 participants, but attendance has gradually declined to a critically low level, with only seven attendees in our last meeting. + +**This situation is hardly sustainable if we aim to deliver the first SONNX profile by the end of the year.** + +So, we urgently need to expand our team to: + +- Continue specifying SONNX operators, +- Finalize the format specification (primarily verification and potential completion), +- Develop the "SONNX checker" tool, +- Alleviate the burden on the core team. + +So far, we have refrained from broadening our audience, assuming the initial group was already sufficiently large and that it would be more effective to engage a wider audience once we had a solid set of preliminary results. While we have not fully reached this point, we are getting close and we plan to get talk during ONNX next meetup in June. + +The key problem is that most of the people have no dedicated funding for this activity, which obviously strongly limit the commitment… **In this situation, having a clear view of what effort you can spend on SONNX would really help sharing the work. Please tell us.** + +If your capability is more of less the time we spend in our meetings, o**ne possibility could be to shorten the meetings and use this time to do actual work on smaller and more focused tasks.** Would you agree? + +Another solution could be to setup some form of collaborative project (funded by the participants and/ secure external funding to allocate dedicated resources for key tasks, thereby lightening the workload of the working group. + +If you have any ideas about this modality, please tell us. + +Finally, we also have to address the root causes for this situation: + +- A general lack of interest in the topic, +- Diverging views on the objectives, +- Differences in the way the working group is managed. + +While there may be little we can do about (i), we can certainly improve (ii) and (iii). + +We welcome your feedback—please let us know what you think could or should be improved. + +Thank you for your cooperation. diff --git a/safety-related-profile/documents/c_code_generation/conv_op/.gitignore b/safety-related-profile/documents/c_code_generation/conv_op/.gitignore new file mode 100644 index 00000000..f12c3561 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/.gitignore @@ -0,0 +1,5 @@ +.why3find +why3session.xml +why3shapes.gz +/lib +/html diff --git a/safety-related-profile/documents/c_code_generation/conv_op/Makefile b/safety-related-profile/documents/c_code_generation/conv_op/Makefile new file mode 100644 index 00000000..ed847a19 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/Makefile @@ -0,0 +1,37 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib cc clean + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +update: + why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" + +EXTRACT=-D c -D cdriver.drv -L . --debug-all --modular --interface -o lib + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr lib + @why3 extract $(EXTRACT) libvector.CIndex + @why3 extract $(EXTRACT) libtensor.CTensor + @cd lib && gcc -c *.c + @find lib -type f | sort + +clean: + rm -fr lib html diff --git a/safety-related-profile/documents/c_code_generation/conv_op/README.md b/safety-related-profile/documents/c_code_generation/conv_op/README.md new file mode 100644 index 00000000..cb07f3ad --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/README.md @@ -0,0 +1,93 @@ +# ONNX-C + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv b/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv new file mode 100644 index 00000000..38cc585f --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/cdriver.drv @@ -0,0 +1,87 @@ +module std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + + + +module std.Cint + interface export "#include " + interface export "#include " + syntax type int "int32_t" + syntax val of_int "(int32_t) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val malloc_int "malloc(%1 * sizeof(int))" + syntax val malloc_int32 "malloc(%1 * sizeof(int32_t))" + syntax val get "%1[%2]" + syntax val ([]) "%1[%2]" prec 1 1 15 +end + +module std.Clib + syntax val malloc_int32 "malloc(%1 * sizeof(int32_t))" +end + +module mach.int.Int32 + syntax type int32 "int32_t" + syntax val of_int "(int32_t) %1" + syntax val to_int "(int) %1" + syntax val (>=) "%1 >= %2" prec 11 11 10 + syntax val (<=) "%1 <= %2" prec 11 11 10 + syntax val (<) "%1 < %2" prec 11 11 10 + syntax val (>) "%1 > %2" prec 11 11 10 + syntax val (=) "%1 == %2" prec 11 11 10 +end + +module mach.int.UInt32 + syntax type uint32 "uint32_t" + syntax val to_int "(int) %1" +end + +module std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module ref.Ref + syntax type ref "%1*" + syntax val ref "&%1" + syntax val (!) "*%1" + syntax val (:=) "*%1 = %2" +end + +module mach.c.C + syntax type ptr "%1*" +end + + +module libvector.CIndex + interface "#include " + interface "#include " +end + +module libtensor.CTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + + +end + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw b/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw new file mode 100644 index 00000000..fd7bcf7b --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use std.Int + use std.List + use tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json new file mode 100644 index 00000000..0b07d2c9 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/layout/proof.json @@ -0,0 +1,39 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CFlat": { + "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 }, + "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.088 }, + "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.16 }, + "offset_push": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.075 }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.6 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.049 } + ] + } + ] + }, + "offset_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { "prover": "z3@4.13.4", "time": 0.01 }, + { "prover": "cvc5@1.2.1", "time": 0.137 } + ] + } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak new file mode 100644 index 00000000..cf0e4c72 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3session.xml.bak @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak new file mode 100644 index 00000000..46419040 Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/layout/why3shapes.gz.bak differ diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw b/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw new file mode 100644 index 00000000..89c0865d --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor.mlw @@ -0,0 +1,313 @@ +module CTensor + use int.Int + use std.List + use std.Clib + use std.Cfloat + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + use ref.Ref + use tensor.OPAdd + use tensor.OPConv2d + + + + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else 0.0 + + function tensor_value (t : ctensor) : list int -> real = + fun k -> tensor_value_at k t + + function tensor_valueb (t : ctensor) : list int -> bool = + fun k -> Real.(tensor_value_at k t > 0.0) + + let ghost function tensor (t : ctensor) : tensor real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = 0.0 ; + } + + let ghost function tensorb (t : ctensor) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_where (cond a b r : ctensor) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + (*qed*) + requires { tensorb cond ~ tensor a ~ tensor b } + ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) } + (*qed*) + +let ctensor_add (a b r : ctensor) = + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + ensures { tensor r = opadd (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + invariant { forall k. + 0 <= k < i -> + value_at r.t_data k = + value_at a.t_data k .+ value_at b.t_data k + } + r.t_data[i] <- a.t_data[i] .+ b.t_data[i] + done + ; + assert { tensor r == opadd (tensor a) (tensor b) } + +(* ===== SECTION 1: Function Definition and Pre/Postconditions ===== *) + + (* Goal: Implements the imperative 2D convolution operation (Conv2D) by iterating over all output pixels, + * calculating the weighted sum, and storing the result in the 'output' tensor. + * Inputs: + * x, w, b: ctensor - Input, Kernel (weights), and Bias tensors. + * pad_top...str_w: int32 - Convolution attributes (Padding, Dilation, Stride). + * output: ctensor - The tensor where the result will be stored (modified in-place). + * Outputs: unit (returns nothing, modifies 'output' in-place). *) +let ctensor_conv2d (x : ctensor) (w : ctensor) (b : ctensor) + (pad_top pad_bottom pad_left pad_right : int32) + (dil_h dil_w : int32) (str_h str_w : int32) + (output : ctensor): unit = + requires { valid_tensor x } + requires { valid_tensor w } + requires { valid_tensor b } + requires { valid_tensor output } + requires { x.t_rank = 4 } (* Input tensor [N, C, H, W] *) + requires { w.t_rank = 4 } (* Kernel tensor [M, C, kH, kW] *) + requires { b.t_rank = 1 } (* Bias tensor [M] *) + requires { output.t_rank = 4 } (* Output tensor [N, M, oH, oW] *) + + + (* Attribute constraints: Stride and Dilation must be positive, Padding must be non-negative. *) + requires { str_h > 0 /\ str_w > 0 } + requires { dil_h > 0 /\ dil_w > 0 } + requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 } + (* Postcondition: The concrete tensor 'output' must be equal to the ghost specification of Conv2D. *) + ensures { tensor output = OPConv2d.opconv2d (tensor x) (tensor w) (tensor b) + (Int32.to_int pad_top) (Int32.to_int pad_bottom) + (Int32.to_int pad_left) (Int32.to_int pad_right) + (Int32.to_int dil_h) (Int32.to_int dil_w) + (Int32.to_int str_h) (Int32.to_int str_w) } + + + (* ===== SECTION 2: Dimension Extraction and Variable Initialization ===== *) + + (* Extraction des dimensions de chaque tenseur pour les limites des boucles *) + let n_batches = x.t_dims[0] in (* Batch size (N) *) + let c_in = x.t_dims[1] in (* Input Channels (C_in) *) + let h_in = x.t_dims[2] in (* Input Height (H_in) *) + let w_in = x.t_dims[3] in (* Input Width (W_in) *) + let m_out = w.t_dims[0] in (* Output Channels / Kernel count (M) *) + let kh = w.t_dims[2] in (* Kernel Height (kH) *) + let kw = w.t_dims[3] in (* Kernel Width (kW) *) + + let h_out = output.t_dims[2] in (* Output Height (oH) *) + let w_out = output.t_dims[3] in (* Output Width (oW) *) + + + (* Conversion des paramètres de convolution en variables locales (int32 -> int32) *) + let pad_top_i = pad_top in + let pad_left_i = pad_left in + let dil_h_i = dil_h in + let dil_w_i = dil_w in + let str_h_i = str_h in + let str_w_i = str_w in + + + (* Allocation de tableaux temporaires pour stocker les coordonnées 4D (X, W, Output) ou 1D (B) + * Ces tableaux sont utilisés par la fonction 'coffset' pour calculer l'indice 1D dans t_data. *) + let x_coords = malloc_int32 (to_uint32 4) in + let w_coords = malloc_int32 (to_uint32 4) in + let b_coords = malloc_int32 (to_uint32 1) in + let output_coords = malloc_int32 (to_uint32 4) in + + if is_not_null x_coords && is_not_null w_coords && is_not_null b_coords && is_not_null output_coords then begin + (* Boucles imbriquées pour calculer la convolution *) + (* ===== SECTION 3: Core Iteration Loops (Output Tensor) ===== *) + for n = 0 to n_batches - 1 do (* Loop over Batch dimension (N) *) + invariant { true } + for m = 0 to m_out - 1 do (* Loop over Output Channels (M) *) + invariant { true } + for oh = 0 to h_out - 1 do (* Loop over Output Height (oH) *) + invariant { true } + for ow = 0 to w_out - 1 do (* Loop over Output Width (oW) *) + invariant { true } + + (* Initialisation de l'accumulateur pour le produit scalaire (dot product) *) + let conv_sum = ref (f64 0.0) in + + (* ===== SECTION 4: Convolution and Summation Loops (Kernel) ===== *) + + for c = 0 to c_in - 1 do (* Loop over Input Channels (C) *) + invariant { true } + for k_h = 0 to kh - 1 do (* Loop over Kernel Height (kH) *) + invariant { true } + for k_w = 0 to kw - 1 do (* Loop over Kernel Width (kW) *) + invariant { true } + + (* 1. Calculate the indices in the input space *with padding* (ih, iw) *) + let ih = oh * str_h_i + k_h * dil_h_i in + let iw = ow * str_w_i + k_w * dil_w_i in + + (* 2. Convert to *original* input indices (after subtracting padding) *) + let padded_ih = ih - pad_top_i in + let padded_iw = iw - pad_left_i in + (* 3. Retrieve the input value (X) with zero-padding handling *) + let x_val = + if 0 <= padded_ih && padded_ih < h_in && 0 <= padded_iw && padded_iw < w_in then begin + (* Calculate the X offset if the index is within the original bounds *) + set_ofs x_coords 0 n; + set_ofs x_coords 1 c; + set_ofs x_coords 2 padded_ih; + set_ofs x_coords 3 padded_iw; + let x_offset = coffset x_coords x.t_dims 4 in + if x_offset >= 0 then x.t_data[x_offset] else f64 0.0 (* Value of X *) + end else f64 0.0 (* Value 0.0 for padding *) + in + + (* 4. Retrieve the kernel value (W) and calculate its offset *) + set_ofs w_coords 0 m; + set_ofs w_coords 1 c; + set_ofs w_coords 2 k_h; + set_ofs w_coords 3 k_w; + let w_offset = coffset w_coords w.t_dims 4 in + let w_val = if w_offset >= 0 then w.t_data[w_offset] else f64 0.0 in + + (* 5. Accumulate the product X_val * W_val into the convolution sum *) + conv_sum := !conv_sum .+ (x_val .* w_val) + done + done + done; + + (* ===== SECTION 5: Final Result Calculation and Assignment ===== *) + + (* 6. Add the bias (B) (corresponding to the output channel m) *) + set_ofs b_coords 0 m; + let b_offset = coffset b_coords b.t_dims 1 in + let bias_val = if b_offset >= 0 then b.t_data[b_offset] else f64 0.0 in + let final_val = !conv_sum .+ bias_val in + + (* 7. Calculate the final offset and store the result in the 'output' data array *) + set_ofs output_coords 0 n; + set_ofs output_coords 1 m; + set_ofs output_coords 2 oh; + set_ofs output_coords 3 ow; + let output_offset = coffset output_coords output.t_dims 4 in + if output_offset >= 0 then output.t_data[output_offset] <- final_val + done + done + done + done + end + + + + +end + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json new file mode 100644 index 00000000..25f011d6 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/proof.json @@ -0,0 +1,2595 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.215 } + ], + "proofs": { + "CITensor": { + "citensor_clear": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + { "prover": "alt-ergo@2.5.4", "time": 0.056 }, + { "prover": "alt-ergo@2.5.4", "time": 0.076 }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + }, + "citensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.054 }, + "citensor_reset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + { "prover": "alt-ergo@2.5.4", "time": 0.058 }, + { "prover": "alt-ergo@2.5.4", "time": 0.087 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + "citensor_to_tensor": { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + "gto_int32": { + "tactic": "split_vc", + "children": [ null, { "prover": "alt-ergo@2.5.4", "time": 0.023 } ] + }, + "to_int32": { "prover": "alt-ergo@2.5.4", "time": 0.021 } + }, + "CTensor": { + "ctensor_add": { "prover": "alt-ergo@2.5.4", "time": 0.87 }, + "ctensor_clear": { "prover": "alt-ergo@2.5.4", "time": 0.157 }, + "ctensor_conv2d": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.068 }, + { "prover": "alt-ergo@2.5.4", "time": 0.059 }, + { "prover": "alt-ergo@2.5.4", "time": 0.059 }, + { "prover": "alt-ergo@2.5.4", "time": 0.06 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.115 }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 }, + { "prover": "alt-ergo@2.5.4", "time": 0.173 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.258 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.404 }, + { "prover": "alt-ergo@2.5.4", "time": 0.048 }, + { "prover": "alt-ergo@2.5.4", "time": 0.631 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.924 }, + { "prover": "alt-ergo@2.5.4", "time": 0.068 }, + { "prover": "cvc5@1.2.1", "time": 0.594 }, + { "prover": "alt-ergo@2.5.4", "time": 0.064 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.636 }, + { "prover": "alt-ergo@2.5.4", "time": 0.625 }, + { "prover": "cvc5@1.2.1", "time": 0.166 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.084 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.079 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.177 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.085 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.187 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.085 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.084 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.662 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.085 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.113 }, + { "prover": "alt-ergo@2.5.4", "time": 0.768 }, + { "prover": "cvc5@1.2.1", "time": 0.104 }, + { "prover": "cvc5@1.2.1", "time": 0.323 }, + { "prover": "alt-ergo@2.5.4", "time": 0.72 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.088 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.087 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.787 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.088 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.09 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.817 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.087 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.088 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.09 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.79 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.09 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.098 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.15 }, + { "prover": "alt-ergo@2.5.4", "time": 0.942 }, + { "prover": "cvc5@1.2.1", "time": 0.107 }, + { "prover": "cvc5@1.2.1", "time": 0.411 }, + { "prover": "alt-ergo@2.5.4", "time": 0.155 }, + { "prover": "cvc5@1.2.1", "time": 0.174 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.173 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.037 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.194 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.182 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.116 }, + { "prover": "alt-ergo@2.5.4", "time": 0.734 }, + { "prover": "cvc5@1.2.1", "time": 0.103 }, + { "prover": "cvc5@1.2.1", "time": 0.366 }, + { "prover": "alt-ergo@2.5.4", "time": 0.116 }, + { "prover": "alt-ergo@2.5.4", "time": 0.075 }, + { "prover": "alt-ergo@2.5.4", "time": 0.067 }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.061 }, + { "prover": "alt-ergo@2.5.4", "time": 0.122 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.155 }, + { "prover": "alt-ergo@2.5.4", "time": 0.098 }, + { "prover": "alt-ergo@2.5.4", "time": 0.759 }, + { "prover": "alt-ergo@2.5.4", "time": 0.125 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.127 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.134 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.134 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.074 }, + { "prover": "alt-ergo@2.5.4", "time": 0.178 }, + { "prover": "alt-ergo@2.5.4", "time": 0.121 }, + { "prover": "alt-ergo@2.5.4", "time": 0.124 }, + { "prover": "cvc5@1.2.1", "time": 0.308 }, + { "prover": "alt-ergo@2.5.4", "time": 0.087 }, + { "prover": "alt-ergo@2.5.4", "time": 0.081 }, + { "prover": "z3@4.13.4", "time": 0.118 }, + { "prover": "alt-ergo@2.5.4", "time": 0.056 }, + { "prover": "alt-ergo@2.5.4", "time": 0.05 }, + { "prover": "alt-ergo@2.5.4", "time": 0.044 }, + { "prover": "alt-ergo@2.5.4", "time": 0.043 }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.119 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.03 }, null + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + null + ] + } + ] + }, + "ctensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "ctensor_reset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.055 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + }, + "ctensor_where": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.061 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.044 }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.22 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 } + ] + }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "tensorb": { "prover": "alt-ergo@2.5.4", "time": 0.028 } + }, + "CTensorConv": { + "ctensor_conv2d": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.066 }, + { "prover": "alt-ergo@2.5.4", "time": 0.065 }, + { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + { "prover": "alt-ergo@2.5.4", "time": 0.064 }, + { "prover": "alt-ergo@2.5.4", "time": 0.067 }, + { "prover": "alt-ergo@2.5.4", "time": 0.071 }, + { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + { "prover": "alt-ergo@2.5.4", "time": 0.068 }, + { "prover": "alt-ergo@2.5.4", "time": 0.065 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "z3@4.13.4", "time": 0.313 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.539 }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 }, + { "prover": "alt-ergo@2.5.4", "time": 0.654 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.854 }, + { "prover": "alt-ergo@2.5.4", "time": 0.056 }, + { "prover": "z3@4.13.4", "time": 0.927 }, + { "prover": "alt-ergo@2.5.4", "time": 0.065 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.8 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.07 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.9 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.074 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.569 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.572 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.397 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.393 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.099 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 0.4 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.101 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.11 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.106 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.102 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.105 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 0.427 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.106 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.103 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.038 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.101 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.109 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.102 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.148 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.047 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.108 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.111 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.099 }, + { "prover": "cvc5@1.2.1", "time": 0.422 }, + { "prover": "cvc5@1.2.1", "time": 0.478 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.103 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.088 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.858 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.091 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 0.938 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.099 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.103 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.098 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 1.9 }, + { "prover": "alt-ergo@2.5.4", "time": 0.1 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 1.9 }, + { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.098 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.098 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.1 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.103 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.156 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.107 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.092 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.099 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.094 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.086 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.101 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.096 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.098 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.088 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.095 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.101 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.093 } + ] + } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.161 }, + { "prover": "cvc5@1.2.1", "time": 0.326 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.316 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 0.305 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "cvc5@1.2.1", "time": 0.468 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.035 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.133 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.089 }, + { "prover": "cvc5@1.2.1", "time": 0.456 }, + { "prover": "alt-ergo@2.5.4", "time": 0.128 }, + { "prover": "alt-ergo@2.5.4", "time": 0.078 }, + { "prover": "alt-ergo@2.5.4", "time": 0.079 }, + { "prover": "alt-ergo@2.5.4", "time": 0.07 }, + { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + { "prover": "alt-ergo@2.5.4", "time": 0.638 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.073 }, + { "prover": "z3@4.13.4", "time": 0.31 }, + { "prover": "alt-ergo@2.5.4", "time": 0.116 }, + { "prover": "alt-ergo@2.5.4", "time": 0.763 }, + { "prover": "alt-ergo@2.5.4", "time": 0.712 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.776 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.835 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.029 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.734 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 } + ] + } + ] + } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.033 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.085 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.039 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.037 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.127 }, + { "prover": "alt-ergo@2.5.4", "time": 0.127 }, + { "prover": "alt-ergo@2.5.4", "time": 0.988 }, + { "prover": "alt-ergo@2.5.4", "time": 0.08 }, + { "prover": "alt-ergo@2.5.4", "time": 0.089 }, + { "prover": "z3@4.13.4", "time": 0.911 }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.055 }, + { "prover": "alt-ergo@2.5.4", "time": 0.054 }, + { "prover": "alt-ergo@2.5.4", "time": 0.041 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + }, + { "prover": "z3@4.13.4", "time": 0.206 }, + null + ] + } + }, + "CTensor_Int": { + "create_coords_array": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.04 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + "create_coords_array_int": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.031 }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + "ctensor_int_clear": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + { "prover": "alt-ergo@2.5.4", "time": 0.045 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.025 } + ] + }, + "ctensor_int_create": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, null, null + ] + } + ] + } + ] + }, + "ctensor_int_get": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.031 }, null + ] + } + ] + }, + "ctensor_int_set": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 }, + { "prover": "cvc5@1.2.1", "time": 0.372 }, + null + ] + }, + "tensor_int": { "prover": "alt-ergo@2.5.4", "time": 0.042 } + }, + "CtensorConv2D": { + "conv_partial_sum_dilated": { + "tactic": "split_vc", + "children": [ + null, + null, + null, + null, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, null + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, null + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, null + ] + }, + null + ] + } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak new file mode 100644 index 00000000..80c59c98 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3session.xml.bak @@ -0,0 +1,1534 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak new file mode 100644 index 00000000..5369bfbf Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/libtensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw b/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw new file mode 100644 index 00000000..2cc06a87 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector.mlw @@ -0,0 +1,136 @@ +(** C-Library to compute Offsets & Dimensions *) + +module CIndex + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use layout.CFlat + + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { pdim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + (** Extracted Library *) + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { dimension u n } + (*qed*) + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> dimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> dimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + + (** ## Coordinates *) + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { dimension ds n } + ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + let ref p = 0 in + for i = 0 to n - 1 do + (*proof*) + invariant { valid (ivector ks i) (ivector ds i) } + invariant { p = offset (ivector ks i) (ivector ds i) } + (*qed*) + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + begin + (*proof*) + assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ; + ghost + begin + ensures { Int32.in_bounds (p * d) } + ensures { Int32.in_bounds (p * d + k) } + let ghost dl = pure { sdim ds 0 i } in + let ghost di = Int32.to_int ds[i] in + let ghost dr = pure { sdim ds (i+1) n } in + let ghost dn = pure { sdim ds 0 n } in + sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * d + k ; + end + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json new file mode 100644 index 00000000..8bfef621 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector/proof.json @@ -0,0 +1,103 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CIndex": { + "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 }, + "cdim_create_2": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.224 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 } + ] + }, + "cdim_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.066 }, + { "prover": "alt-ergo@2.5.4", "time": 0.935 }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.193 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 } + ] + }, + "coffset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.813 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.057 }, + { "prover": "alt-ergo@2.5.4", "time": 0.191 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, null + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.93 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 } + ] + }, + "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 }, + "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak new file mode 100644 index 00000000..fbd37417 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3session.xml.bak @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak new file mode 100644 index 00000000..03a81a28 Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/libvector/why3shapes.gz.bak differ diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std.mlw b/safety-related-profile/documents/c_code_generation/conv_op/std.mlw new file mode 100644 index 00000000..5d88c4c9 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/std.mlw @@ -0,0 +1,231 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + use list.List + use list.Length + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () +(** Helper function to extract element from list at given position *) + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + type uint32 = UInt32.uint32 + + val function to_uint32 (v : int32) : uint32 + requires { 0 <= v } + ensures { Int32.to_int v = UInt32.to_int result } + + (** Null-Pointer *) + val predicate is_null (vp : ptr 'a) : bool + ensures { result <-> vp.zone = null_zone } + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset + p /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + + val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + +end + +module Cint + use int.Int + use mach.int.Int32 + use mach.int.Int32 as Int32 + use mach.int.UInt32 + use mach.int.UInt32 as UInt32 + use int.ComputerDivision + use Clib + + val function ( .+ ) (a b : int) : int + ensures { result = a + b } + + val function ( .- ) (a b : int) : int + ensures { result = a - b } + + val function ( .* ) (a b : int) : int + ensures { result = a * b } + + val function ( ./ ) (a b : int) : int + requires { b <> 0 } + ensures { result = ComputerDivision.div (a) ( b) } + + val function of_int (v : int) : Int32.int32 + ensures { Int32.to_int result = v } + + val function malloc_int (s: uint32) : ptr int + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + val function get (p: ptr int) (k: Int32.int32) : int + ensures { result = p[k] } + + val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + let ([]) (p : ptr int) (k :Int32.int32) : int = get p k + +end + + +module Cfloat + use int.Int + use real.Real + + type float = abstract { to_real : real } + meta coercion function to_real + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + val function f32 (k : f32) : float + ensures { result = f32'real k } + + val function f64 (k : f64) : float + ensures { result = f64'real k } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { Real.( result = a + b ) } + + val function (.-) (a b : float) : float + ensures { Real.( result = a - b ) } + + val function (.-_) (a : float) : float + ensures { Real.( result = (- a) ) } + + val function ( .* ) (a b : float) : float + ensures { Real.( result = a * b ) } + + val function ( ./ ) (a b : float) : float + requires { b <> 0.0 } + ensures { Real.( result = a / b ) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> Real.( a = b ) } + + val predicate ( .< ) (a b : float) : bool + ensures { result <-> Real.( a < b ) } + + val predicate ( .<= ) (a b : float) : bool + ensures { result <-> Real.( a <= b ) } + +end diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json new file mode 100644 index 00000000..5cdde76b --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/std/proof.json @@ -0,0 +1,28 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "Cfloat": { + "one": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 } + }, + "Cint": { "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.026 } }, + "Clib": { + "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "slice": { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "vector_push": { "prover": "z3@4.13.4", "time": 0.027 } + }, + "Int": { + "euclide": { "prover": "z3@4.13.4", "time": 0.02 }, + "get_dim": { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 } + }, + "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.017 } } + } +} diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak new file mode 100644 index 00000000..562e22e5 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/std/why3session.xml.bak @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak new file mode 100644 index 00000000..cc61e1dc Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/std/why3shapes.gz.bak differ diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw b/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw new file mode 100644 index 00000000..e137cdaf --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor.mlw @@ -0,0 +1,318 @@ +(** Formalization of coordinates and dimensions *) + +module Range + use int.Int + use std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma valid_push (ks ds : list int) (k d : int) + requires { 0 <= k < d } + requires { valid ks ds } + ensures { valid (push ks k) (push ds d) } + (*proof*) + variant { ks } + = match ks , ds with + | Nil , Nil -> () + | Cons _ ks , Cons _ ds -> valid_push ks ds k d + | _ -> absurd + end + (*qed*) + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + +end + +(** Formalization of Tensor *) +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + background : 'a ; (* default value, or value for 0-dimensions tensor *) + } + invariant { positive dims } + invariant { forall k. valid k dims \/ data k = background } + (*proof*) + by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d } + (*qed*) + + meta coercion function dims + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + + (** Tensor with the same dimensions and background value *) + predicate (~=) (a : tensor 'a) (b : tensor 'a) = + a ~ b /\ a.background = b.background + + (** Equal tensors *) + predicate (==) (a b : tensor 'a) = + a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k + + (** Extensionality *) + lemma exteq: forall a b : tensor 'a. a == b <-> a = b + (*proof*) by tensor'eq a b (*qed*) + + (** Scalar Tensor *) + let ghost function scalar (v : 'a) : tensor 'a + ensures { result.dims = Nil } + ensures { result.background = v } + ensures { forall k. result k = v } + (*proof*) + = { dims = Nil ; data = (fun _ -> v) ; background = v } + (*qed*) + + (** Null Tensor *) + let ghost function zero (e : 'a) (ds : list int) : tensor 'a + requires { positive ds } + ensures { result.dims = ds } + ensures { result.background = e } + ensures { forall k. result k = e } + (*proof*) + = { dims = ds ; data = (fun _ -> e) ; background = e } + (*qed*) + + + (** Constant Tensor *) + let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a + ensures { result.dims = ds } + requires { positive ds } + ensures { result.background = bg } + ensures { forall k. valid k ds -> result k = v } + (*proof*) + = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg } + (*qed*) + + (** Constant & Null *) + goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds + +end + +(** OP-Where Tensor Operation *) +module OPWhere + use Tensor + + let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) + +end + +module OPAdd + use Tensor + use int.Int + use real.Real + use std.Cfloat + + let ghost function dadd (a b : data real) : data real + = fun ks -> a ks + b ks + + + let ghost function opadd (a b : tensor real) : tensor real + requires { a ~= b } + ensures { result ~= a } + ensures { result.data = dadd a.data b.data } + = { dims = a.dims ; + data = dadd a.data b.data ; + background = a.background + b.background } + +end + +module OPConv2d + use Tensor + use Range + use int.Int + use real.Real + use std.List + use std.Int + use std.Cint + use list.List + use list.Length + use list.Nth + use std.Cfloat + use ref.Ref + + + +(* ===== SECTION 1: Helper function for padding (Zero-Padding) ===== *) + +(** A helper function to access the padded input tensor value. *) +(* Goal: Retrieves a value from the input tensor X, accounting for zero-padding. + * If the effective coordinates (x_h, x_w), after removing padding, fall outside the original tensor bounds, it returns 0.0. + * Inputs: + * x: data real - The input tensor data function. + * x_dims: list int - The dimensions of the input tensor X [N, C, H, W]. + * pad_top, pad_left: int - The padding applied to the top/left of the height/width dimensions. + * n, c: int - Batch and input channel indices. + * x_h, x_w: int - The coordinates in the conceptually padded input space (derived from output position, stride, and dilation). + * Outputs: real - The value from the input tensor, or 0.0 if padded. *) + let ghost function padded_value (x : data real) (x_dims : list int) + (pad_top pad_left : int) (n c x_h x_w : int) : real = + requires { length x_dims = 4 } + let padded_h = x_h - pad_top in (* Convert padded index to original index *) + let padded_w = x_w - pad_left in (* Convert padded index to original index *) + let padded_ks = Cons n (Cons c (Cons padded_h (Cons padded_w Nil))) in + if valid padded_ks x_dims then + x padded_ks (* Return value if coordinates are within bounds *) + else + 0.0 (* Return 0.0 if coordinates are out of bounds *) + +(* --- *) + +(* ===== SECTION 2: Data-level Convolution (Weighted Sum) ===== *) + +(** The main 2D convolution data operation. *) +(* Goal: Defines the function that computes the value of a single output pixel Y[n, m, y_h, y_w]. + * This is achieved by iterating over all input channels (c), kernel height (i), and kernel width (j), performing the dot product, and adding the bias. + * Inputs: Tensors (data functions) X, W, B, their dimensions, and all convolution attributes. + * Outputs: data real - The data function for the output tensor Y. *) + let ghost function dconv2d (x : data real) (x_dims : list int) + (w : data real) (w_dims : list int) (b : data real) (b_dims : list int) + (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int) + (str_h str_w : int) : data real + requires { length x_dims = 4 } (* X shape: [N, C, H, W] *) + requires { length w_dims = 4 } (* W shape: [M, C, kH, kW] where M=C_out *) + requires { length b_dims = 1 } (* [M, C, kH, kW] *) + requires { get_dim x_dims 1 = get_dim w_dims 1 } (* C_in = C_kernel *) + requires { get_dim w_dims 0 = get_dim b_dims 0 } (* M = M_bias (Output Channels must match Bias size) *) + requires { str_h > 0 /\ str_w > 0 } + requires { dil_h > 0 /\ dil_w > 0 } + = fun yks -> (* Input 'yks' is the output coordinate list: [n, m, y_h, y_w] *) + if length yks <> 4 then 0.0 else + let Cons n (Cons m (Cons y_h (Cons y_w Nil))) = yks in (* Destructure output coordinates *) + let c_in = get_dim w_dims 1 in (* Number of input channels *) + let kh = get_dim w_dims 2 in (* Kernel height *) + let kw = get_dim w_dims 3 in (* Kernel width *) + + let sum_value = ref 0.0 in (* Accumulator for the convolution sum *) + for c = 0 to c_in - 1 do (* Loop over input channels *) + invariant { true } + for i = 0 to kh - 1 do (* Loop over kernel height (i) *) + invariant { true } + for j = 0 to kw - 1 do (* Loop over kernel width (j) *) + invariant { true } + (* Calculate corresponding H/W coordinate in the padded input X *) + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + j * dil_w in + let x_val = padded_value x x_dims pad_top pad_left n c x_h x_w in (* Get padded input value *) + let w_coords = Cons m (Cons c (Cons i (Cons j Nil))) in + let w_val = w w_coords in (* Get kernel weight value *) + sum_value := !sum_value + (x_val * w_val) (* Accumulate the product *) + done + done + done; + let bias_coords = Cons m Nil in + let bias_val = b bias_coords in (* Get the bias value for output channel m *) + !sum_value + bias_val (* Final output value = Weighted Sum + Bias *) + +(* --- *) + +(* ===== SECTION 3: Tensor-level Convolution Operator and Shape Calculation ===== *) +(** The tensor-level 2D convolution operator. *) +(* Goal: Constructs the final output tensor Y, calculating its dimensions and assigning the dconv2d data function. + * Inputs: The three tensors X, W, B, and all convolution attributes. + * Outputs: tensor real - The resulting output tensor Y [N_out, C_out, H_out, W_out]. *) + let ghost function opconv2d (x w b : tensor real) + (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int) + (str_h str_w : int) : tensor real + requires { length x.dims = 4 } (* [N, C, H, W] *) + requires { length w.dims = 4 } (* [M, C, kH, kW] *) + requires { length b.dims = 1 } (* [M] *) + requires { get_dim x.dims 1 = get_dim w.dims 1 } (* C_in = C_kernel *) + requires { get_dim w.dims 0 = get_dim b.dims 0 } (* M = M_bias *) + requires { str_h > 0 /\ str_w > 0 } + requires { dil_h > 0 /\ dil_w > 0 } + requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 } + ensures { length result.dims = 4 } + ensures { get_dim result.dims 0 = get_dim x.dims 0 } (* N_out = N_in *) + ensures { get_dim result.dims 1 = get_dim w.dims 0 } (* C_out = M *) + (* Main ensures clause: the output data must be exactly the function dconv2d *) + ensures {result.data = dconv2d x.data x.dims w.data w.dims b.data b.dims + pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w} + (*proof*) + = let n = get_dim x.dims 0 in + let c_in = get_dim x.dims 1 in + let h_in = get_dim x.dims 2 in + let w_in = get_dim x.dims 3 in + let m_out = get_dim w.dims 0 in + let kh = get_dim w.dims 2 in + let kw = get_dim w.dims 3 in + + (* Calculate Effective Kernel Size (with dilation) *) + let effective_kh = (kh - 1) * dil_h + 1 in + let effective_kw = (kw - 1) * dil_w + 1 in + (* Calculate Output Height (H_out) using the convolution formula: + * H_out = floor((H_in + P_top + P_bottom - effective_kH) / S_h) + 1 *) + let out_h = (h_in + pad_top + pad_bottom - effective_kh) ./ (str_h + 1) in + (* Calculate Output Width (W_out) *) + let out_w = (w_in + pad_left + pad_right - effective_kw) ./ (str_w + 1) in + let output_dims = Cons n (Cons m_out (Cons out_h (Cons out_w Nil))) in + (* Return the final output tensor record *) + { dims = output_dims ; + data = dconv2d x.data x.dims w.data w.dims b.data b.dims + pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w ; + + background = 0.0 } + +end + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json b/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json new file mode 100644 index 00000000..0c02b926 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor/proof.json @@ -0,0 +1,256 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "OPAdd": { + "dadd": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "opadd": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + null, + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 } + ] + } + }, + "OPConv2d": { + "dconv2d": { "prover": "alt-ergo@2.5.4", "time": 0.113 }, + "opconv2d": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + null, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.197 }, + { "prover": "z3@4.13.4", "time": 0.047 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + }, + "padded_value": { "prover": "alt-ergo@2.5.4", "time": 0.02 } + }, + "OPWhere": { + "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 } + }, + "OpConv2D": { + "conv2d": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + null, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.2 }, + { "prover": "alt-ergo@2.5.4", "time": 0.606 } + ] + }, + "conv_at_position_dilated": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, null + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, null + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, null + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 } + ] + }, + "conv_sum_dilated": { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, null + ] + } + ] + } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, null + ] + } + ] + } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.015 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 } + ] + }, + { "prover": "cvc5@1.2.1", "time": 0.029 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.014 } + ] + }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.018 } + ] + } + ] + }, + null + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + } + }, + "Range": { + "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 }, + "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "valid_push": { "prover": "alt-ergo@2.5.4", "time": 0.033 } + }, + "Tensor": { + "const": { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + "exteq": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 } + ] + }, + "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero_is_const": { "prover": "alt-ergo@2.5.4", "time": 0.014 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak new file mode 100644 index 00000000..6b8f7fcb --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3session.xml.bak @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak new file mode 100644 index 00000000..1f0f73ed Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/conv_op/tensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/documents/c_code_generation/conv_op/why3find.json b/safety-related-profile/documents/c_code_generation/conv_op/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/conv_op/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/documents/c_code_generation/sources.md b/safety-related-profile/documents/c_code_generation/sources.md new file mode 100644 index 00000000..94c56df6 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/sources.md @@ -0,0 +1,151 @@ +# Converting Why3 Specifications to C Code + +This document outlines how to extract C code from Why3 (`.mlw`) specifications using the Why3 extraction mechanism and a C driver. It includes an example, supported features, and key constraints. + +## Compiling Why3 to C language +### Example + +This example defines a function `locate_max` that returns the index of the maximum element in an array `a` of size `n`. + +```whyml +use int.Int +use map.Map as Map +use mach.c.C +use mach.int.Int32 +use mach.int.Int64 + +function ([]) (a: ptr 'a) (i: int): 'a = Map.get a.data.Array.elts (a.offset + i) + +let locate_max (a: ptr int64) (n: int32): int32 + requires { 0 < n } + requires { valid a n } + ensures { 0 <= result < n } + ensures { forall i. 0 <= i < n -> a[i] <= a[result] } += let ref idx = 0 in + for j = 1 to n - 1 do + invariant { 0 <= idx < n } + invariant { forall i. 0 <= i < j -> a[i] <= a[idx] } + if get_ofs a idx < get_ofs a j then idx <- j + done; + idx + ``` + +### Extraction Command +```bash +why3 extract -D c locate_max.mlw +``` +With debug details: +```bash +why3 extract -D c --debug-all locate_max.mlw -o locate_max.c +``` +### Output C code +```c +#include + +int32_t locate_max(int64_t * a, int32_t n) { + int32_t idx; + int32_t j, o; + idx = 0; + o = n - 1; + if (1 <= o) { + for (j = 1; ; ++j) { + if (a[idx] < a[j]) { + idx = j; + } + if (j == o) break; + } + } + return idx; +} +``` + +## Supporte Features and Rules +### Basic Types + +| WhyML Type | Description | C Equivalent | +|----------------------|------------------------------------|------------------------| +| `int32`, `uint64`, etc. | From `mach.int` | `int32_t`, `uint64_t`, etc. | +| `bool` | Translated from `bool.Bool` | `int` | +| `char`, `string` | Partial support via `mach.c.String`| `char`, `char *` | +| `int` (mathematical) | ❌ Not supported | — | +| `float` | ❌ Not supported | — | +--- + +### Compound Types +#### Immutable Records + +**WhyML:** +```whyml +type r = { x : int32; y : int32 } +let swap (a : r) : r = { x = a.y ; y = a.x } +``` +**C:** +```c +struct r { + int32_t x; + int32_t y; +}; + +struct r swap(struct r a) { + struct r r; + r.x = a.y; + r.y = a.x; + return r; +} +``` + +#### Mutable Records +**WhyML:** +```whyml +type r = { mutable x : int32; mutable y : int32 } +let swap (a : r) : unit = + let tmp = a.y in a.y <- a.x; a.x <- tmp +``` +**C:** +```c +struct r { + int32_t x; + int32_t y; +}; + +void swap(struct r * a) { + int32_t tmp; + tmp = a->y; + a->y = a->x; + a->x = tmp; +} +``` +## Unsupported Features +- WhyML arrays + +- Algebraic data types (including enumerations) + +- Pattern matching + +- Exception raising and catching + +- Floating-point types + +## Control Stuctures + +| Feature | Support | +|----------------------------|---------| +| `if` / `else` | ✅ | +| `while` loops | ✅ | +| `for` loops | ✅ | +| Sequence (`;`) | ✅ | +| `break`, `continue`, `return` | ✅ | +| Pattern matching | ❌ | +| Exceptions | ❌ | + +## Notes +- **Example of driver file used for C extraction**: [`c.drv`](https://gitlab.inria.fr/why3/why3/-/blob/master/drivers/c.drv) +- This driver defines how WhyML types and functions are translated into equivalent C constructs. + +## References + +- [Why3 Documentation](https://why3.lri.fr/doc/) +- [Why3 GitLab Repository](https://gitlab.inria.fr/why3/why3) + + + diff --git a/safety-related-profile/documents/c_code_generation/tensor/.gitignore b/safety-related-profile/documents/c_code_generation/tensor/.gitignore new file mode 100644 index 00000000..f12c3561 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/.gitignore @@ -0,0 +1,5 @@ +.why3find +why3session.xml +why3shapes.gz +/lib +/html diff --git a/safety-related-profile/documents/c_code_generation/tensor/Makefile b/safety-related-profile/documents/c_code_generation/tensor/Makefile new file mode 100644 index 00000000..4ca6970b --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/Makefile @@ -0,0 +1,37 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib cc clean + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +update: + why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" + +EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr lib + @why3 extract $(EXTRACT) libvector.CIndex + @why3 extract $(EXTRACT) libtensor.CTensor + @cd lib && gcc -c *.c + @find lib -type f | sort + +clean: + rm -fr lib html diff --git a/safety-related-profile/documents/c_code_generation/tensor/README.md b/safety-related-profile/documents/c_code_generation/tensor/README.md new file mode 100644 index 00000000..cb07f3ad --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/README.md @@ -0,0 +1,93 @@ +# ONNX-C + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv b/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv new file mode 100644 index 00000000..b2ac835a --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/cdriver.drv @@ -0,0 +1,35 @@ +module std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module libvector.CIndex + interface "#include " + interface "#include " +end + +module libtensor.CTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/documentation.md b/safety-related-profile/documents/c_code_generation/tensor/documentation.md new file mode 100644 index 00000000..59d987cc --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/documentation.md @@ -0,0 +1,128 @@ +# Formally Verified C Tensor Library + +This project is a provably correct tensor library written in C, with its correctness formally verified using **Why3** and its **WhyML language**. +The primary goal is to demonstrate how formal verification can be applied to ensure that low-level C code for numerical operations behaves exactly as intended by a high-level mathematical model. + +--- + +## Project Structure + +These files provide a **formal verification system** for a tensor library, primarily using the **WhyML language** with **Why3**. +The goal is to **prove the correctness of C-like tensor operations**. +This repository contains: + - Formal specification files (`.mlw`) + - The C driver file (`.drv`) + - A `Makefile` to automate the process + +### File Breakdown + +#### `tensor.mlw` +This file is the **core of the formalization**, defining the fundamental mathematical concepts and data structures for tensors. +It serves as a **foundational library** for other modules. + +- **Range module** + - `size`: Calculates the total number of elements in a tensor (recursive multiplication of dimensions). + - `positive`: Predicate that checks all dimensions are positive integers. + - `valid`: Verifies coordinates are within the valid range for given dimensions. + +- **Tensor module** + - `data`: Represents tensor elements as a map from coordinates to values. + - `tensor`: Record with dimensions (`dims`), data (`data`), and background value. + - **Predicates**: `~`, `~=`, `==` define equality and similarity notions for tensors. + - **Ghost Functions**: they are for verification only and won't be compiled to C + - `scalar`: Single-valued tensor + - `zero`: Tensor of all zeros + - `const`: Tensor with a constant value + +--- + +#### `layout.mlw` +This file (module **CFlat**) focuses on the **memory layout of tensors**. + +- **Functions** + - `offset`: Maps multidimensional coordinates to a flat memory address (generalized row-major offset). + - `index`: Inverse of `offset`, mapping an address back to coordinates. + +- **Lemmas** + - `index_of_offset`: Proves correctness and reversibility of `offset` and `index`. + +--- + +#### `libvector.mlw` +This module bridges **abstract concepts** with **concrete C arrays**. + +- **Types and Functions** + - `iarray`: Pointer to a C array of 32-bit integers. + - `ivector`, `islice`: Convert `iarray` to WhyML lists. + - `cdim_size`: C implementation of total size calculation, proven equivalent to the abstract definition. + - `coffset`: C implementation of memory offset, proven equivalent to `layout.mlw`’s `offset`. + +--- + +#### `libtensor.mlw` +Provides a **C-like representation of tensors** and proofs linking them to abstract models. + +- **Types** + - `ctensor`: Concrete C representation (rank, dimensions array, data array). + +- **Ghost Functions** + - `tensor`, `tensorb`: Convert `ctensor` into abstract `tensor`. + +- **C Functions (with proofs)** + - `ctensor_clear`: Fills tensor with zeros → proven equivalent to `Tensor.zero`. + - `ctensor_reset`: Fills tensor with constant → proven equivalent to `Tensor.const`. + - `ctensor_where`: Conditional element assignment → proven equivalent to `opwhere`. + +--- + +#### `cdriver.drv` +A **Why3 driver file** that specifies mappings from WhyML to C. + +- **std.Clib**: Maps standard C library functions (`stdlib.h`, `stdint.h`). +- **std.Cfloat**: Maps floating-point operations (`+`, `-`, `*`, `/`) to C equivalents. +- **Other modules**: Headers like `cindex.h` for extracted code. + +--- + +#### `Makefile` +Automates the **build and verification process**. + +- `prove`: Runs Why3 proofs on all `.mlw` files. +- `doc`: Generates HTML documentation. +- `lib`: Extracts verified WhyML to C and compiles into a library. +- `clean`: Removes generated files. + +--- +### Dependency Diagram + +![Project Dependency Graph](images/diagram.png) + +--- + +## Building and Verification + +The `Makefile` simplifies the entire process with a few commands: + +### Prove all properties +```bash +make prove +``` +This command runs the Why3 proof engine on all the .mlw files to check that all logical properties and correctness conditions hold. +### Generate the C library: +```bash +make lib +``` +This command first extracts the verified WhyML code into C source files and then compiles them into a library. + +### Generate documentation +```bash +make doc +``` +This command creates an HTML documentation from the formal specifications in the WhyML files. + +### Clean the project +```bash +make clean +``` +Removes all generated files, including the C library and documentation. and insert the figure in the right place + diff --git a/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png b/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png new file mode 100644 index 00000000..d2b2d284 Binary files /dev/null and b/safety-related-profile/documents/c_code_generation/tensor/images/diagram.png differ diff --git a/safety-related-profile/documents/c_code_generation/tensor/layout.mlw b/safety-related-profile/documents/c_code_generation/tensor/layout.mlw new file mode 100644 index 00000000..fd7bcf7b --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use std.Int + use std.List + use tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json b/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json new file mode 100644 index 00000000..0b07d2c9 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/layout/proof.json @@ -0,0 +1,39 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CFlat": { + "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 }, + "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.088 }, + "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.16 }, + "offset_push": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.075 }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.6 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.049 } + ] + } + ] + }, + "offset_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { "prover": "z3@4.13.4", "time": 0.01 }, + { "prover": "cvc5@1.2.1", "time": 0.137 } + ] + } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw b/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw new file mode 100644 index 00000000..c150fcb5 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/libtensor.mlw @@ -0,0 +1,140 @@ +module CTensor + use std.Int + use std.List + use std.Clib + use std.Cfloat + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else 0.0 + + function tensor_value (t : ctensor) : list int -> real = + fun k -> tensor_value_at k t + + function tensor_valueb (t : ctensor) : list int -> bool = + fun k -> Real.(tensor_value_at k t > 0.0) + + let ghost function tensor (t : ctensor) : tensor real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = 0.0 ; + } + + let ghost function tensorb (t : ctensor) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_where (cond a b r : ctensor) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + (*qed*) + requires { tensorb cond ~ tensor a ~ tensor b } + ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) } + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json b/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json new file mode 100644 index 00000000..94f77db9 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/libtensor/proof.json @@ -0,0 +1,46 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.215 } + ], + "proofs": { + "CTensor": { + "ctensor_clear": { "prover": "alt-ergo@2.5.4", "time": 0.157 }, + "ctensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "ctensor_reset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.055 }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + }, + "ctensor_where": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.061 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.062 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.044 }, + { "prover": "alt-ergo@2.5.4", "time": 0.063 }, + { "prover": "alt-ergo@2.5.4", "time": 0.22 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 } + ] + }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "tensorb": { "prover": "alt-ergo@2.5.4", "time": 0.028 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw b/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw new file mode 100644 index 00000000..79ff9b42 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/libvector.mlw @@ -0,0 +1,135 @@ +(** C-Library to compute Offsets & Dimensions *) + +module CIndex + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use layout.CFlat + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { pdim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + (** Extracted Library *) + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { dimension u n } + (*qed*) + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> dimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> dimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + + (** ## Coordinates *) + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { dimension ds n } + ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + let ref p = 0 in + for i = 0 to n - 1 do + (*proof*) + invariant { valid (ivector ks i) (ivector ds i) } + invariant { p = offset (ivector ks i) (ivector ds i) } + (*qed*) + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + begin + (*proof*) + assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ; + ghost + begin + ensures { Int32.in_bounds (p * d) } + ensures { Int32.in_bounds (p * d + k) } + let ghost dl = pure { sdim ds 0 i } in + let ghost di = Int32.to_int ds[i] in + let ghost dr = pure { sdim ds (i+1) n } in + let ghost dn = pure { sdim ds 0 n } in + sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * d + k ; + end + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json b/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json new file mode 100644 index 00000000..be619b08 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/libvector/proof.json @@ -0,0 +1,104 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CIndex": { + "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 }, + "cdim_create_2": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.224 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 } + ] + }, + "cdim_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.066 }, + { "prover": "alt-ergo@2.5.4", "time": 0.935 }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.193 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 } + ] + }, + "coffset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.813 }, + { "prover": "alt-ergo@2.5.4", "time": 0.026 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.057 }, + { "prover": "alt-ergo@2.5.4", "time": 0.191 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 1.9 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.93 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + { "prover": "alt-ergo@2.5.4", "time": 0.011 } + ] + }, + "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 }, + "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/tensor/std.mlw b/safety-related-profile/documents/c_code_generation/tensor/std.mlw new file mode 100644 index 00000000..0a25aefd --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/std.mlw @@ -0,0 +1,179 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + type uint32 = UInt32.uint32 + + val function to_uint32 (v : int32) : uint32 + requires { 0 <= v } + ensures { Int32.to_int v = UInt32.to_int result } + + (** Null-Pointer *) + val predicate is_null (vp : ptr 'a) : bool + ensures { result <-> vp.zone = null_zone } + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset + p /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + +end + +module Cfloat + use int.Int + use real.Real + + type float = abstract { to_real : real } + meta coercion function to_real + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + val function f32 (k : f32) : float + ensures { result = f32'real k } + + val function f64 (k : f64) : float + ensures { result = f64'real k } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { Real.( result = a + b ) } + + val function (.-) (a b : float) : float + ensures { Real.( result = a - b ) } + + val function (.-_) (a : float) : float + ensures { Real.( result = (- a) ) } + + val function ( .* ) (a b : float) : float + ensures { Real.( result = a * b ) } + + val function ( ./ ) (a b : float) : float + requires { b <> 0.0 } + ensures { Real.( result = a / b ) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> Real.( a = b ) } + + val predicate ( .< ) (a b : float) : bool + ensures { result <-> Real.( a < b ) } + + val predicate ( .<= ) (a b : float) : bool + ensures { result <-> Real.( a <= b ) } + +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/std/proof.json b/safety-related-profile/documents/c_code_generation/tensor/std/proof.json new file mode 100644 index 00000000..e12bdd27 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/std/proof.json @@ -0,0 +1,26 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "Cfloat": { + "one": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 } + }, + "Clib": { + "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "slice": { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "vector_push": { "prover": "z3@4.13.4", "time": 0.027 } + }, + "Int": { + "euclide": { "prover": "z3@4.13.4", "time": 0.02 }, + "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 } + }, + "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.017 } } + } +} diff --git a/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw b/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw new file mode 100644 index 00000000..abf3ce30 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/tensor.mlw @@ -0,0 +1,159 @@ +(** Formalization of coordinates and dimensions *) + +module Range + use int.Int + use std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma valid_push (ks ds : list int) (k d : int) + requires { 0 <= k < d } + requires { valid ks ds } + ensures { valid (push ks k) (push ds d) } + (*proof*) + variant { ks } + = match ks , ds with + | Nil , Nil -> () + | Cons _ ks , Cons _ ds -> valid_push ks ds k d + | _ -> absurd + end + (*qed*) + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + +end + +(** Formalization of Tensor *) +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + background : 'a ; (* default value, or value for 0-dimensions tensor *) + } + invariant { positive dims } + invariant { forall k. valid k dims \/ data k = background } + (*proof*) + by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d } + (*qed*) + + meta coercion function dims + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + + (** Tensor with the same dimensions and background value *) + predicate (~=) (a : tensor 'a) (b : tensor 'a) = + a ~ b /\ a.background = b.background + + (** Equal tensors *) + predicate (==) (a b : tensor 'a) = + a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k + + (** Extensionality *) + lemma exteq: forall a b : tensor 'a. a == b <-> a = b + (*proof*) by tensor'eq a b (*qed*) + + (** Scalar Tensor *) + let ghost function scalar (v : 'a) : tensor 'a + ensures { result.dims = Nil } + ensures { result.background = v } + ensures { forall k. result k = v } + (*proof*) + = { dims = Nil ; data = (fun _ -> v) ; background = v } + (*qed*) + + (** Null Tensor *) + let ghost function zero (e : 'a) (ds : list int) : tensor 'a + requires { positive ds } + ensures { result.dims = ds } + ensures { result.background = e } + ensures { forall k. result k = e } + (*proof*) + = { dims = ds ; data = (fun _ -> e) ; background = e } + (*qed*) + + + (** Constant Tensor *) + let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a + ensures { result.dims = ds } + requires { positive ds } + ensures { result.background = bg } + ensures { forall k. valid k ds -> result k = v } + (*proof*) + = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg } + (*qed*) + + (** Constant & Null *) + goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds + +end + +(** OP-Where Tensor Operation *) +module OPWhere + use Tensor + + let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json b/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json new file mode 100644 index 00000000..aa543a48 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/tensor/proof.json @@ -0,0 +1,35 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "OPWhere": { + "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 } + }, + "Range": { + "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 }, + "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "valid_push": { "prover": "alt-ergo@2.5.4", "time": 0.033 } + }, + "Tensor": { + "const": { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + "exteq": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 } + ] + }, + "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "zero_is_const": { "prover": "alt-ergo@2.5.4", "time": 0.014 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/tensor/why3find.json b/safety-related-profile/documents/c_code_generation/tensor/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/tensor/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore b/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore new file mode 100644 index 00000000..f12c3561 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/.gitignore @@ -0,0 +1,5 @@ +.why3find +why3session.xml +why3shapes.gz +/lib +/html diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/Makefile b/safety-related-profile/documents/c_code_generation/where_op/main/Makefile new file mode 100644 index 00000000..09a822a5 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/Makefile @@ -0,0 +1,36 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib clean + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l + +update: + why3find prove *.mlw -l -s -m -x + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" + +EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr lib + @why3 extract $(EXTRACT) libvector.CIndex + @cd lib && gcc -c *.c + @find lib -type f + +clean: + rm -fr lib html diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/README.md b/safety-related-profile/documents/c_code_generation/where_op/main/README.md new file mode 100644 index 00000000..cb07f3ad --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/README.md @@ -0,0 +1,93 @@ +# ONNX-C + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv b/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv new file mode 100644 index 00000000..01aec189 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/cdriver.drv @@ -0,0 +1,11 @@ +module std.Clib + interface export "#include " + interface export "#include " + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module libvector.CIndex + interface "#include " + interface "#include " +end diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw new file mode 100644 index 00000000..fd7bcf7b --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use std.Int + use std.List + use tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json new file mode 100644 index 00000000..e35cde60 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/layout/proof.json @@ -0,0 +1,39 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CFlat": { + "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.055 }, + "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.197 }, + "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.343 }, + "offset_push": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.075 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.6 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.049 } + ] + } + ] + }, + "offset_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "z3@4.13.4", "time": 0.02 }, + { "prover": "cvc5@1.2.1", "time": 0.137 } + ] + } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw new file mode 100644 index 00000000..f2f0c591 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/libvector.mlw @@ -0,0 +1,118 @@ + +(** Offset & Dimensions *) +module CIndex + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use layout.CFlat + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ vdim u n <= max_int32 + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { pdim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + (** Extracted Library *) + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { dimension u n } + (*qed*) + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> dimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> dimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + + (** ## Coordinates *) + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { valid_range ds 0 n } + requires { vdim ds n < max_int32 } + ensures { 0 <= result -> dimension ds n } + ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + assume { false } ; + let ref p = 0 in + for i = 0 to n - 1 do + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + p <- p * d + k + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json new file mode 100644 index 00000000..6505f2d5 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/libvector/proof.json @@ -0,0 +1,72 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CIndex": { + "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.194 }, + "cdim_create_2": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.461 }, + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.031 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 }, + { "prover": "alt-ergo@2.5.4", "time": 0.03 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + "cdim_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.031 }, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.066 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ { "prover": "alt-ergo@2.5.4", "time": 1.4 } ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.097 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.037 }, + { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + { "prover": "alt-ergo@2.5.4", "time": 0.193 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + }, + "coffset": { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.074 }, + "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.206 } + } + } +} diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw new file mode 100644 index 00000000..8889dc17 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/std.mlw @@ -0,0 +1,124 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json new file mode 100644 index 00000000..d2bc32ea --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/std/proof.json @@ -0,0 +1,22 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "Clib": { + "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + "slice": { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "vector_push": { "prover": "z3@4.13.4", "time": 0.027 } + }, + "Int": { + "euclide": { "prover": "z3@4.13.4", "time": 0.02 }, + "mult_bound": { "prover": "z3@4.13.4", "time": 0.012 } + }, + "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.035 } } + } +} diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw b/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw new file mode 100644 index 00000000..b25db8ce --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/tensor.mlw @@ -0,0 +1,88 @@ +module Range + use int.Int + use std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + +end + +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + } invariant { positive dims } + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + +end + +module OPWhere + use Tensor + + let function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { c ~ a ~ b } + ensures { result ~ c ~ a ~ b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data } + (*qed*) + +end diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json b/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json new file mode 100644 index 00000000..a2f9be46 --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/tensor/proof.json @@ -0,0 +1,16 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } ], + "proofs": { + "OPWhere": { + "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.019 } + }, + "Range": { + "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.033 }, + "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.015 } + }, + "Tensor": { "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.015 } } + } +} diff --git a/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json b/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/documents/c_code_generation/where_op/main/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/documents/conv_specification_example/README.md b/safety-related-profile/documents/conv_specification_example/README.md deleted file mode 100644 index e78e70c5..00000000 --- a/safety-related-profile/documents/conv_specification_example/README.md +++ /dev/null @@ -1,708 +0,0 @@ - -# /!\ WARNING /!\ -THIS VERSION IS NOW OBSOLETE. -PLEASE DO NOT MAKE ANY MODIFICATIONS TO THIS VERSION.\ -INSTEAD, USE THE VERSION LOCATED [HERE](../profile_opset/conv/conv.md) - - -# Conventions -## Notations -- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$. If the tensor represents an image, $h(X)$ and $w(X)$ represent the _height_ and the _width_ of the image. -## Usage of fonts -- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`. -## Tags -- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number.\ -A synthesis of all restrictions is given in section "Restrictions". -## Types -- Operators are first described for values in the domain of real numbers. A specific description is given for the other types (floats, integers). - -# `conv` operator (real) - -### Restrictions -The following restrictions apply to the `conv` operator for the SONNX profile: -- The number of spatial axes of the tensors is restricted to 2 `[R1]` -- Attribute `auto_pad` is restricted to `NOTSET` `[R2]` -- Attribute `group` is restricted to 1 (standard convolution) or to the number of channels of the input tensor (depthwise convolution) `[R3]` -- All attributes shall be be given explicit values (i.e., default values for attributes are not supported) `[R4]` -- The number of elements in the `strides` list is equal to 2. `[R5]` - -### Signature -`Y = conv(X,W,[B])` -where -- `X`: input tensor -- `W`: convolution kernel -- `B`: optional bias -- `Y`: output tensor - -#### Informal specification - -Operator `conv` computes the convolution of the input tensor `X` with the kernel `W` and adds bias `B` to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_ `[R3]`. The SONNX profile limits the number of spatial axes to 2 `[R1]`. - -##### Standard convolution -A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where `group`= 1. - -The mathematical definition of the operator without padding is given hereafter. -The formal specification is given in Section Formal specification. When considering padding, the same formula applies, in which `X` represents the padded version of the actual input `X`. - -$$\begin{gathered} - Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} \\ (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) \\ + B[c] -\end{gathered}$$ - -Where -- $b$ is the batch index, $b \in [0,b(Y)-1]$, $b(Y)$ is the batch size of output `Y` -- $c$ is the data channel, $c \in [0,c(Y)-1]$, $c(Y)$ is the number of data channels of output `Y` -- $m \in [0,h(Y)-1]$ is the index of the first spatial axis of output `Y` -- $n \in [0,w(Y)-1]$ is the index of the second spatial axis of output `Y` -- $fm(W)$ is the number of feature maps of kernel `W` -- $h(W)$ is the size of the first spatial axis of kernel `W` -- $w(W)$ is the sizes of the second spatial axis of kernel `W` - -`strides` and `dilations` are attributes of the operator. They are described later in this section. - -The effect of the operator is illustrated on the following figure. In this example -- shape of `Y` is $1\times 1 \times 4 \times 4$ (batch size is 1, number of data channels is 1) -- shape of `X` is $1 \times 1 \times 8 \times 8$ (batch size is 1, number of data channels is 1) -- shape of `W` is $1 \times 1 \times 3 \times 2$ (number of data channels is 1) -- shape of `B` is $1$ -- `pads` is set to (1,2,2,2) (1 column on the left, 2 columns on the right, 2 rows on the top, 2 rows on the bottom) -- `dilations` is set to (2,2) -- `strides` is set to (2,3) - -![](./imgs/conv-std.png) - -The following figure shows the case where the number of channels of `X` is 3. In this example: -- shape of `Y` is $1 \times 1 \times 4 \times 4$ -- shape of `X` is $1 \times 3 \times 8 \times 8$ -- shape of `W` is $1 \times 3 \times 3 \times 2$ -- shape of `B` is $1$ -- `groups` is set to 1 -- the other attributes have the same values as in the previous figure. - -![](./imgs/conv-std-3-channels.png) - - -##### Depthwise convolution -A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where `group`= $c(X)$. - -The mathematical definition is given hereafter: - -$$\begin{gathered} - Y[b, c, m, n] = \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1}\\ (X[b, c, m \cdot strides[0] + j \cdot dilations[0], n \cdot strides[1] + z \cdot dilations[1]] \cdot W[c, 0, j , z] ) + B[c] -\end{gathered}$$ - -Variables are defined as for the standard convolution. -The effect of the operator is illustrated on the following figure. In this example, -- shape of `Y` is $1\times 3 \times 4 \times 4$ -- shape of `X` is $1 \times 3 \times 8 \times 8$ -- shape of `W` is $3 \times 1 \times 3 \times 2$ -- shape of `B` is $3$ -- `groups` is set to 3 -- the other attributes have the same values as in the previous figure. - -![](./imgs/conv-dep-3-channels.png) - -#### Inputs and outputs - -##### `X` - -Tensor `X` is the input tensor on which convolution with kernel `W` is computed. - -The shape of tensor `X` is $b(X) \times c(X) \times h(X) \times w(X)$. - -###### Constraints - -- (C1) Number of spatial axes of tensor `X` - - Statement: The number of spatial axes of tensor `X` is 2. `[R1]` - - Rationale: This restriction is intoduced to simplify the implementation considering the actual industrial use cases. -- (C2) Consistency between the number of channels of `X` and `W` - - Statement: $c(X)=fm(W)$ -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` - - - Statement: - * $$\left\lfloor{\frac{alpha-(dilations[0] \cdot h(W)-1)}{strides[0]}} \right\rfloor +1 = h(Y) \mbox{ with } alpha=h(X)+pads[0]+pads[2]$$ - - and - - * $$\left\lfloor{\frac{beta-(dilations[1] \cdot w(W)-1)}{strides[1]}} \right\rfloor +1 = w(Y) \mbox{ with } beta=w(X)+pads[1]+pads[3]$$ - - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis. -- (C4) Axis denotations - - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\]. - - Rationale: Denotation convention - -##### `W` - -Tensor `W` is the convolution kernel. - -The shape of tensor `W`is $(c(W) \times fm(W) \times h(W) \times w(W))$, where -- $c(W)$ is the number of output channels or number of feature maps -- $fm(W)$ is the number of input channels -- $h(W)$ and $w(W)$ are the sizes of the kernel for the two spatial axes. - -###### Constraints -- (C1) Consistency between the number of channels of `X` and `W` - - Statement: [See constraint (C2) of X](#channel_consist). -- (C2) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist). -- (C3) Consistency between `W` and `kernel_shape` - - - Statement: The size of `W` for an axis must bve equal to the value of `kernel_shape` for that axis - - Rationale: `kernel_shape` represents the shape of `W`, where `kernel_shape[0]` = $w(W)$ and `kernel_shape[1]` = $h(W)$. -- (C4) Axis denotations - - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\]. - - Rationale: Denotation convention - -##### `B` - -Tensor `B` is the bias. - -The shape of tensor `B`is $c(B)$. - -###### Constraints -- (C1) Consistency between the number of channels of `B` and `W` - - Statement: $c(B) = fm(W)$. - -#### Attributes - -##### `strides`: list of int - -Attribute `strides` determines how the kernel is applied on tensor `X` during the convolution. - -For instance, with $\mbox{\texttt{stride}}[0]=2$ and $\mbox{\texttt{stride}}[1]=3$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution. - -> The previous sentence is not clear... - -The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(2,3). - - - -###### Constraints -- (C1) Size of `strides` - - Statement: the number of elements in the `strides` list is equal to 2. `[R5]` - - Rationale: The SONNX profile only supports 2 spatial axes. -- (C2) Value domain - - Statement: `strides` is a list of strictly positive integers. - - Rationale: Stride values are used in the denominator of expression in [constraint (C3) of X](#shape_consist) -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `auto_pad` : string - -The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X. - -###### Constraints -- (C1) Explicit padding - - Statement: `auto_pad` shall be set to `NOTSET` `[R3]` - - Rationale: The SONNX profile imposes explicit padding. - -##### `pads`: list of int - -Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor `X`. - -`pads` is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$. - -The padding value is 0. - -The effect of the `pads` attribute is illustrated on the following figure. In this example, `pads`=(1,3,2,2). - - - -###### Constraints -- (C1) Value domain - - Statement: `pads` is a list of positive or null integers. - - Rationale: A padding value gives a number of elements to be added to some spatial axis. This is positive[^2]. -- (C2) Consistency between the shape of `X` and the length of `pads` - - Statement: The length of the `pads` list is two times the number of spatial axes of `X` - - Rationale: Padding shall be given for all spatial axes, and a beggining value and an end value must be given for each axis. -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `dilations`: list of int - -Attribute `dilations` specifies the spacing between the kernel elements for each spatial axis of the filter `W`. It is a list of non-null integer values where each value gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel points are spaced out by the dilation factor for that axis. - -The spacing value is 0. - -The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. In this example, `dilations`=(2,2). - - - - -###### Constraints -- (C1) Value domain - - Statement: `dilations` is a list of strictly positive integers -- (C2) Relation between `dilations` and `W` - - Statement: The length of the `dilations` list is equal to number of spatial axes of `W`. - - Rationale: Dilation is defined for all spatial axes of `W`. -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `group`: int - -This attribute specifies the number of groups the input channels and output channels are divided into. When `group`=1, a standard convolution is performed. -When group is greater than 1, convolution is computed for each group separately with a specific set of filters. - -The effect of the `group` attribute for a tensor with two spatial axes is depicted on the following figure. In this example `group`=3. - - - -(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning) - -In the example, with `group` set to 3 and an input `X` and an output `Y` with 3 channels, the input and output channels will be divided into 3 groups of 1 channel. - -###### Constraints -- (C1) Support for standard and depthwise convolutions - - Statement: `group`=1 (standard convolution) or `group`$=c(X)$ (depthwise convolution) - - Rationale: SONNX only supports standard and depthwise convolutions - -##### `kernel_shape`: list of int - -This parameter specifies the shape of the convolution kernel `W`. - -###### Constraints. - -- (C1) Value domain - - Statement: `kernel_shape` is a list of strictly positive integers - - Rationale: A dimension is always positive and cannot be null. -- (C2) Consistency between `W` and `kernel_shape` - - Statement: [See constraint (C3) of W](#kernel_shape_w) - -#### Outputs - -##### `Y` - -The size of the output `Y` will be $(b(Y) \times c(Y) \times h(Y) \times w(Y))$ where -- $b(Y)$ is the number of batches -- $c(Y)$ is the number of channels -- $h(Y)$ and $w(Y)$ are the sizes of the output for the two spatial axes - -###### Constraints. -- (C1) Consistency between the number of channels of `B` and `Y` - - Statement: HYPERLIEN VERS W / constraint kernel shape -- (C2) Consistency between the shape of tensors `X`, `W`, `Y`, attributes `pads` and `strides`, - - Statement: [see constraint (C3) of X](#shape_consist) - -#### Formal specification - -The following code specifies the `conv` operator using the Why3 language[^3]. - -###### Nota: the specification does not cover all attributes values. Currently, there is no padding (`pads` is not set and `auto_pad = NOTSET`) and `dilations` is not set. - -``` ocaml -module Conv - use int.Int - use real.RealInfix - use array.Array - use int.ComputerDivision - use real.Truncate - use real.FromInt - - type input_tensor = { - x: array real; - x_l: int; - x_c: int; - x_b: int; - x_ch: int; - } - - type convolution_kernel = { - w: array real; - w_l: int; - w_c: int; - w_ch_in: int; - w_ch_out: int; - } - - type bias_tensor = { - b: array real; - b_c: int; - } - - type attributes = { - stride: array int; - pads: array int; - auto_pad: int; - dilation: array int; - } - - type output_tensor = { - - y_b: int; - y_ch: int; - y_l: int; - y_c: int; - - } - - function conv_size (out: output_tensor) : int = - out.y_b * out.y_ch * out.y_l * out.y_c - - predicate conv_result - (inp: input_tensor) - (kernel: convolution_kernel) - (bias: bias_tensor) - (attr: attributes) - (out: output_tensor) - (res: array real) - (bi ci hi wi: int) - (ci_in ki_h ki_w: int) = - let y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi in - let x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] in - let x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] in - - (0 <= x_l_idx < inp.x_l /\ 0 <= x_c_idx < inp.x_c) -> - let x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx in - let w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w in - res.elts (y_idx) = bias.b[ci] +. (inp.x[x_idx] *. kernel.w[w_idx]) - - val conv (inp: input_tensor)(kernel: convolution_kernel)(bias: bias_tensor)(attr: attributes)(out: output_tensor): array real - requires{inp.x_ch = out.y_ch = kernel.w_ch_in = bias.b_c} - requires{out.y_l = (div (inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l)) attr.stride[0]) + 1} - requires{out.y_c = (div (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c)) attr.stride[1]) +1} - requires { inp.x_l > 0 /\ inp.x_c > 0 /\ inp.x_ch > 0 /\ inp.x_b > 0} - requires{kernel.w_l > 0 /\ kernel.w_c > 0 /\ kernel.w_ch_in > 0 /\ kernel.w_ch_out > 0} - requires { out.y_b > 0 /\ out.y_ch > 0 /\ out.y_l > 0 /\ out.y_c > 0} - requires { length inp.x = inp.x_l * inp.x_c * inp.x_ch * inp.x_b} - requires{length kernel.w = kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out} - requires{inp.x_l >= kernel.w_l} - requires{inp.x_c >= kernel.w_c} - requires{length bias.b = bias.b_c} - requires{length attr.stride = 2} - requires{length attr.dilation = 2} - requires{length attr.pads = 4} - requires{forall i. 0 <= i < length attr.pads -> attr.pads[i] = 0} - requires{forall j. 0 <= j < length attr.dilation -> attr.dilation[j] >= 1} - requires{forall k. 0 <= k < length attr.stride -> attr.stride[k] >= 1} - ensures { length result = conv_size out } - ensures { forall bi ci hi wi ci_in ki_h ki_w: int. - 0 <= bi < out.y_b -> - 0 <= ci < out.y_ch -> - 0 <= hi < out.y_l -> - 0 <= wi < out.y_c -> - 0 <= ci_in < kernel.w_ch_in -> - 0 <= ki_h < kernel.w_l -> - 0 <= ki_w < kernel.w_c -> conv_result inp kernel bias attr out result bi ci hi wi ci_in ki_h ki_w } - -end - - -module Test_conv - use int.Int - use real.RealInfix - use array.Array - use int.ComputerDivision - use real.Truncate - use real.FromInt - use Conv - -let test_conv () = - let inp_x = Array.make 9 1.0 in - let inp = { x = inp_x; x_l = 3; x_c = 3; x_b = 1; x_ch = 1 } in - - let kernel_w = Array.make 4 0.0 in - let kernel = { w = kernel_w; w_l = 2; w_c = 2; w_ch_in = 1; w_ch_out = 1 } in - - let bias_b = Array.make 1 0.5 in - let bias = { b = bias_b; b_c = 1 } in - let stride = Array.make 2 1 in (* Stride of 1 *) - let pads = Array.make 4 0 in (* No padding *) - let dilation = Array.make 2 1 in (* Dilation of 1 *) - let attr = { stride = stride; pads = pads; auto_pad = 0; dilation = dilation } in - let out_h = (div (inp.x_l + pads[0] + pads[2] - (dilation[0] * kernel.w_l)) stride[0]) + 1 in - let out_w = (div (inp.x_c + pads[1] + pads[3] - (dilation[1] * kernel.w_c)) stride[1]) + 1 in - let out = { y_b = 1; y_ch = 1; y_l = out_h ; y_c = out_w } in - (* Call the conv function *) - let result = conv inp kernel bias attr out in - let actual_result = result.elts 0 in - assert { conv_result inp kernel bias attr out result 0 0 0 0 0 0 0} ; - assert { actual_result = 0.5 } ; - () - -end -``` - -Another formal specification of the `conv` operator using Frama-C -language[^4] is presented below. - -``` objectivec -#include -#include -#include - -/* Data Structures */ -typedef struct { - float *x; - int x_l, x_c, x_b, x_ch; -} input_tensor; - -typedef struct { - float *w; - int w_l, w_c, w_ch_in, w_ch_out; -} convolution_kernel; - -typedef struct { - float *b; - int b_c; -} bias_tensor; - -typedef struct { - int *stride; - int *pads; - int *dilation; -} attributes; - -typedef struct { - float *y; - int y_b, y_ch, y_l, y_c; -} output_tensor; -/*@ - requires \valid_read(pads + (0 .. 3)); - requires \valid_read(stride + (0 .. 1)); - requires \valid_read(result + (0 .. 3)); - requires x_l > 0 && x_c > 0 && w_l > 0 && w_c > 0 && y_l > 0 && y_c > 0; - assigns result[0 .. 3]; - behavior empty_or_notset: - assumes (auto_pad == "") || (auto_pad == "NOTSET"); - ensures \forall integer i; 0 <= i < 4 ==> result[i] == pads[i]; - -behavior valid: - assumes (auto_pad == "VALID") ; - ensures \forall integer i; 0 <= i < 4 ==> result[i] == 0; - -behavior same_upper: - assumes (auto_pad == "SAME_UPPER") ; - ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l; - \let pad_c = (y_c - 1) * stride[1] + w_c - x_c; - ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==> - (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)) && - (pad_l % 2 != 0) && (pad_c % 2 != 0) ==> - (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == ((pad_c / 2) + 1) && result[3] == ((pad_l / 2) + 1)); - -behavior same_lower: - assumes (auto_pad == "SAME_LOWER"); - ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l; - \let pad_c = (y_c - 1) * stride[1] + w_c - x_c; - ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==> - (result[0] == (pad_c / 2) && result[2] == (pad_c / 2) && result[1] == (pad_l / 2) && result[3] == (pad_l == 2)) && - ((pad_l % 2 != 0) && (pad_c % 2 != 0)) ==> - (result[0] == ((pad_c / 2) + 1) && result[1] == ((pad_l / 2) + 1) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)); -complete behaviors empty_or_notset, valid, same_upper, same_lower; -disjoint behaviors empty_or_notset, valid, same_upper, same_lower; -*/ -void compute_pad(const char* auto_pad, int pads[4], int stride[2], int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int result[4]) { - int pad_l, pad_c; - - if ((auto_pad == "") || (auto_pad == "NOTSET")) { - for (int i = 0; i < 4; i++) { - result[i] = pads[i]; - } - } else if ((auto_pad == "VALID")) { - for (int i = 0; i < 4; i++) { - result[i] = 0; - } - } else if ((auto_pad == "SAME_UPPER")) { - pad_l = (y_l - 1) * stride[0] + w_l - x_l; - pad_c = (y_c - 1) * stride[1] + w_c - x_c; - - if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) { - result[0] = result[2] = pad_c / 2; - result[1] = result[3] = pad_l / 2; - } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) { - result[0] = pad_c / 2; - result[1] = pad_l / 2; - result[2] = (pad_c / 2) + 1; - result[3] = (pad_l / 2) + 1; - } - } else if ((auto_pad == "SAME_LOWER")) { - pad_l = (y_l - 1) * stride[0] + w_l - x_l; - pad_c = (y_c - 1) * stride[1] + w_c - x_c; - - if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) { - result[0] = result[2] = pad_c / 2; - result[1] = result[3] = pad_l / 2; - } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) { - result[0] = (pad_c / 2) + 1; - result[1] = (pad_l / 2) + 1; - result[2] = pad_c / 2; - result[3] = pad_l / 2; - } - } -} - -/* Function to compute the convolution */ -//void compute_pad(int auto_pad, int *pads, int *stride, int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int *result); - -/*@ - requires \valid_read(inp.x + (0..(inp.x_l*inp.x_c*inp.x_b*inp.x_ch)-1)); - requires \valid_read(kernel.w + (0..(kernel.w_l*kernel.w_c*kernel.w_ch_in*kernel.w_ch_out)-1)); - requires \valid_read(bias.b + (0..bias.b_c-1)); - requires \valid_read(attr.stride+(0..1)); - requires \valid_read(attr.pads+(0..3)); - requires \valid_read(attr.dilation+(0..1)); - requires inp.x_ch == out.y_ch; - requires out.y_ch == kernel.w_ch_in; - requires kernel.w_ch_in == bias.b_c; - requires out.y_l == ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1; - requires out.y_c == ((inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) + 1; - requires inp.x_l > 0 && inp.x_c > 0 && inp.x_ch > 0 && inp.x_b > 0; - requires kernel.w_l > 0 && kernel.w_c > 0 && kernel.w_ch_in > 0 && kernel.w_ch_out > 0; - requires bias.b_c > 0; - requires out.y_l > 0 && out.y_c > 0 && out.y_ch > 0 && out.y_b > 0; - requires inp.x_l >= kernel.w_l; - requires inp.x_c >= kernel.w_c; - requires \forall integer i; 0 <= i < 4 ==> attr.pads[i] >= 0; - requires \forall integer i; 0 <= i < 2 ==> attr.dilation[i] >= 1; - requires \forall integer i; 0 <= i < 2 ==> attr.stride[i] >= 1; - - - assigns out.y[0..(out.y_b * out.y_ch * out.y_l * out.y_c)-1]; - - ensures \forall integer bi, ci, hi, wi, ci_in, ki_h, ki_w; - 0 <= bi < out.y_b ==> - 0 <= ci < out.y_ch ==> - 0 <= hi < out.y_l ==> - 0 <= wi < out.y_c ==> - 0 <= ci_in - 0 <= ki_h < kernel.w_l ==> - 0 <= ki_w < kernel.w_c ==> - (0 <= hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) && - (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] < inp.x_l) && - (0 <= wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]) && - (wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] < inp.x_c) ==> - out.y[bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi] == inp.x[bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) * inp.x_c + ( wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1])] * kernel.w[ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w] + bias.b[ci]; - - - - - - - - */ -float* conv(input_tensor inp, convolution_kernel kernel, bias_tensor bias, attributes attr, output_tensor out) { - - out.y_l = ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1; - out.y_c = ( (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) +1; - int y_size = out.y_b * out.y_ch * out.y_l * out.y_c; - out.y = (float *)malloc(y_size * sizeof(float)); - - if (out.y == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(EXIT_FAILURE); - } - - // Compute padding - // compute_pad(attr.auto_pad, attr.pads, attr.stride, inp.x_l, inp.x_c, kernel.w_l, kernel.w_c, out.y_l, out.y_c, attr.pads); - - // Initialize result tensor to bias values - for (int bi = 0; bi < out.y_b; ++bi) { - for (int ci = 0; ci < out.y_ch; ++ci) { - for (int hi = 0; hi < out.y_l; ++hi) { - for (int wi = 0; wi < out.y_c; ++wi) { - int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi; - out.y[y_idx] = bias.b[ci]; - } - } - } - } - - // Convolution computation - for (int bi = 0; bi < out.y_b; ++bi) { - for (int ci = 0; ci < out.y_ch; ++ci) { - for (int hi = 0; hi < out.y_l; ++hi) { - for (int wi = 0; wi < out.y_c; ++wi) { - int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi; - - for (int ci_in = 0; ci_in < kernel.w_ch_in; ++ci_in) { - for (int ki_h = 0; ki_h < kernel.w_l; ++ki_h) { - for (int ki_w = 0; ki_w < kernel.w_c; ++ki_w) { - int x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]; - int x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]; - - if (x_l_idx >= 0 && x_l_idx < inp.x_l && x_c_idx >= 0 && x_c_idx < inp.x_c) { - int x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx; - int w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w; - - if (x_idx < inp.x_l * inp.x_c * inp.x_ch * inp.x_b && w_idx < kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out) { - out.y[y_idx] += inp.x[x_idx] * kernel.w[w_idx]; - } - } - } - } - } - } - } - } - } - - return out.y; -} -``` - -# Graph execution semantics - -
- -Elements of the execution semantics is given on the [IR (Intermediate -Representation) page](https://onnx.ai/onnx/repo-docs/IR.html) of the -ONNX web site. In addition, a Python “reference implementation” is also -provided (see ). The source -code of this implementation can be found at -. - -Very informally, the semantics is pretty simple: each operator (or -function) is called according to its position in the topological sorting -of the operators. The topological order is a partial order that ensures -that an operator is executed only when its inputs are available. Being a -partial order, it means that several valid orders exist for a given -graph. Normally (?) each order should generate the same result, even in -the presence of floating point operations. - -The Python code to execute a graph is given in class -[`ReferenceEvaluator`](https://github.com/onnx/onnx/blob/main/onnx/reference/reference_evaluator.py)). -After having processed the inputs and the initializers (i.e., fed the -`results` dictorionary with these data), the nodes are executed in -sequence. For each operator, the interpretor checks that its inputs are -in the `results` dictionary. If they are not, an error is raised (if the -operators are listed in topological order, this situation should not -occur). Otherwise, the operator is simply executed (method `run`) with -or without a context (composed of the current results) depending on the -type of operators. (Check that this does not create a dependency to the -total order of operators.) - -
- -### Informal specification - -
- -The semantics of an ONNX model is given in Section "Model Semantics" of -the [Intermediate -Representation](https://github.com/onnx/onnx/blob/main/docs/IR.md) page. -Basically, an inference-model is a stateless function (except possibly -for some specific nodes such as a random-generation node) represented by -an acyclic `graph` of nodes. The `graph` is mainly represented by a set -of inputs and outputs and a topologically sorted list of nodes. Each -node represents a call to an operator or a function. A `function` is -itself a graph. - -Note that the types of inputs and outputs are not systematically -required because they can be inferred. In our case, I guess that we will -forbib shape inference and rely on static tensor shapes (or, at least, -shape inference can be bone before serializing the model). The proecss -of shape inference is described in Section  [ONNX Shape -Inference](https://onnx.ai/onnx/repo-docs/ShapeInference.html). - -
- -### Formal specification - -*To be completed.* - -[^1]: At least in a first phase... - -[^2]: Note: in the ONNX runtime implementation, the padding value may be - negative, which corresponds to reducing the size of the tensor. - -[^3]: See [Why3 documentation](https://www(W)hy3.org/) - -[^4]: See [Frama-C - documentation](https://www.frama-c.com/html/documentation.html) diff --git a/safety-related-profile/documents/issues.md b/safety-related-profile/documents/issues.md deleted file mode 100644 index 516c7dfd..00000000 --- a/safety-related-profile/documents/issues.md +++ /dev/null @@ -1 +0,0 @@ -File moved [here](../deliverables/issues/issues.md). \ No newline at end of file diff --git a/safety-related-profile/documents/needs.md b/safety-related-profile/documents/needs.md deleted file mode 100644 index 08d9779d..00000000 --- a/safety-related-profile/documents/needs.md +++ /dev/null @@ -1 +0,0 @@ -File moved [here](../deliverables/needs/needs.md). \ No newline at end of file diff --git a/safety-related-profile/documents/profile_formal/README.md b/safety-related-profile/documents/profile_formal/README.md new file mode 100644 index 00000000..d635b2ce --- /dev/null +++ b/safety-related-profile/documents/profile_formal/README.md @@ -0,0 +1 @@ +This directory contains the formal specification of operators, expressed using the Why3 formal language. \ No newline at end of file diff --git a/safety-related-profile/documents/profile_formal/onnxgraph.mlw b/safety-related-profile/documents/profile_formal/onnxgraph.mlw new file mode 100644 index 00000000..642920dc --- /dev/null +++ b/safety-related-profile/documents/profile_formal/onnxgraph.mlw @@ -0,0 +1,604 @@ +module Graph + + +use int.Int +use list.List +use list.Map +use option.Option +use map.Map +use list.Mem +use list.FoldLeft +use list.Length +use list.NthNoOpt +use map.Const +use bool.Bool + + +(* A tensor value is just an int [preliminary]*) +type value = int + +(* A tensor is designated by an integer id [preliminary] *) +(* In the actual implementation, a tensor is designted by a string *) +(* [TXX] *) +type tensor = int + +(* The state of a graph is a mapping from the set of tensors to a set optional values *) +(* [TXX] *) +type graph_state = Map.map tensor (option value) + +(* An operator *) +(* [TODO] The inputs / outputs of an operator are typed. *) +(* [TODO] The operator may have parameters, e.g., a convolution kernel size *) +(* [TODO] The operator may have attributes, e.g., a relu activation function *) +(* [TODO] The operator may have a type, e.g., a convolution, an addition, etc. *) +(* [TODO] The operator may have a set of attributes, e.g., a relu activation function *) + +(* The binding proposed by a node must comply with these typing constraints *) +type shape = list int (* dimensions of the tensor *) + +(* An operator is a function that takes a list of input tensors and produces a list of output tensors *) +(* [TXX] The operator is defined by its name, its input shapes, and its output shapes *) +type operator = { + name: string; (* name of the operator *) + opi: list shape; (* input shapes *) + opo: list shape; (* output shapes *) +} + +(* ------------------------------------------------------------------------- *) +(* [TXX] An operator has at least one output *) +(* ------------------------------------------------------------------------- *) +predicate operator_one_output (op: operator) = + length op.opo > 0 + + +goal operator_one_output_ok : + let op : operator = { + name="Add" ; + opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ; + opo= (Cons (Cons 1 Nil) Nil) + } in + operator_one_output op + +goal operator_one_output_ko : + let op : operator = { + name="Add" ; + opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ; + opo= Nil + } in + not operator_one_output op + + +(* A node is an application of an operator *) +(* [TXX] *) +type node = { + ope: operator; (* The operator referred to by the node *) + oi: list tensor; (* Input tensors, position-wise *) + ou: list tensor; (* Output tensors, position-wise *) +} + + +(* A graph is a list of tensors and nodes + a list of input and output tensors *) +(* [TXX] *) +type graph = { + gi: list tensor; (* graph inputs *) + go: list tensor; (* graph outputs *) + gt: list tensor; (* graph tensors *) + gn: list node; (* graph nodes *) +} + + +(* ------------------------------------------------------------------------- *) +(* [TXX] In a graph, a node provide all operators inputs and ouputs *) +(* ------------------------------------------------------------------------- *) +predicate all_operator_ins_and_outs (g: graph) = + forall n: node. mem n g.gn -> + length n.oi = length n.ope.opi /\ length n.ou = length n.ope.opo + +(* Specification tests *) +goal all_operator_ins_and_outs_ok : + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { + ope={ + name="Add" ; + opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ; + opo= (Cons (Cons 1 Nil) Nil) }; + oi=Cons 1 (Cons 2 Nil); + ou= (Cons 4 Nil)} Nil; + } in + all_operator_ins_and_outs g + +goal all_operator_ins_and_outs_ko : + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { + ope={ + name="Add" ; + opi= Cons (Cons 1 Nil) (Cons (Cons 1 Nil) Nil) ; + opo= (Cons (Cons 1 Nil) Nil) }; + oi=Cons 1 (Cons 2 Nil); + ou= Nil} Nil; (* <= the is no output *) + } in + not all_operator_ins_and_outs g + + + +(* ------------------------------------------------------------------------- *) +(* [TXX] In a graph, a node has at least one output tensor*) +(* ------------------------------------------------------------------------- *) +predicate all_node_with_one_output (g: graph) = + forall n: node. mem n g.gn -> n.ou <> Nil + +(* Specification tests *) +goal all_node_with_one_output_ok: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} + (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil); + } in + all_node_with_one_output g + +(* Specification tests *) +goal all_node_with_one_output_ko: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Nil} (* This operator has no output *) + (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil); + } in + not all_node_with_one_output g + + +(* ------------------------------------------------------------------------- *) +(* [TXX] In a graph, a tensor is either an input or the output of at most one node *) +(* ------------------------------------------------------------------------- *) +predicate tensor_is_unique_output (g: graph) = + forall t: tensor. + mem t g.gt -> + (mem t g.gi \/ + (exists n: node. + mem n g.gn /\ mem t n.ou /\ + (forall n': node. + (mem n' g.gn /\ mem t n'.ou) -> n = n'))) + + +(* Specification tests *) +goal tensor_is_unique_output_test_ok: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} + (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 8 Nil)} Nil); + } in + tensor_is_unique_output g + +(* [TODO] This test and its negation are not valid!! *) +goal tensor_is_unique_output_test_ko: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} + (Cons { ope={ name="Sub" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 6 Nil); ou=Cons 7 (Cons 4 Nil)} Nil); + } in + tensor_is_unique_output g + + +(* ------------------------------------------------------------------------- *) +(* [TXX] A graph input is the input of at least one node *) +(* (No useless input) *) +(* ------------------------------------------------------------------------- *) +predicate graph_ins_are_node_ins (g: graph) = +forall t: tensor. + mem t g.gi -> + exists op. mem op g.gn /\ mem t op.oi + +(* Specification tests *) +goal graph_ins_are_node_ins_test_ok: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil; + } in + graph_ins_are_node_ins g + +goal graph_ins_are_node_ins_test_ko: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 5 (Cons 2 Nil); ou= Cons 3 Nil } Nil; + } in + not graph_ins_are_node_ins g + +(* ------------------------------------------------------------------------- *) +(* [TXX] A graph output is the output of one node *) +(* ------------------------------------------------------------------------- *) +predicate graph_outs_are_node_outs (g: graph) = + forall t: tensor. mem t g.go -> + exists n. mem n g.gn /\ mem t n.ou + +(* Specification tests *) +goal graph_outs_are_node_outs_test_ok: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil; + } in + graph_outs_are_node_outs g + +goal graph_outs_are_node_outs_test_ko: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Cons 3 Nil } Nil; + } in + not graph_outs_are_node_outs g + + +(* ------------------------------------------------------------------------- *) +(* [TXX] Graph inputs and ouputs belong to the set of tensors *) +(* ------------------------------------------------------------------------- *) +predicate graph_ins_outs_are_tensors (g: graph) = + forall t: tensor. (mem t g.gi \/ mem t g.go) -> mem t g.gt + +(* Specification tests *) +goal graph_ins_outs_are_tensors_test_ok: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); + gt = Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou=Cons 3 (Cons 4 Nil)} Nil; + } in + graph_ins_outs_are_tensors g + +goal graph_ins_outs_are_tensors_test_ko: + let g : graph = { + gi = Cons 1 (Cons 2 Nil); + go = Cons 3 (Cons 4 Nil); (* <= Output 4 is not in the tensor set *) + gt = Cons 1 (Cons 2 (Cons 3 (Cons 5 Nil))); + gn = Cons { ope={ name="Add" ; opi=Nil; opo=Nil }; oi=Cons 1 (Cons 2 Nil); ou= Cons 3 Nil } Nil; + } in + not graph_ins_outs_are_tensors g + + +(* ------------------------------------------------------------------------- *) +(* [TXX] A tensor is either a graph input or a node output *) +(* ------------------------------------------------------------------------- *) +predicate no_free_tensor (g: graph) = + forall t: tensor. mem t g.gt -> + mem t g.gi \/ + exists n. mem n g.gn \/ mem t n.ou + + + +(* =========================================================================== *) +(* Utilities *) +(* =========================================================================== *) + +(* Create t list of n elements with the same value x *) +let rec make_list (x: 'a) (n: int) : list 'a +requires { n >= 0 } +ensures{ length result = n} +ensures{ forall v. mem v result -> v = x } +variant { n } += + if n = 0 + then + Nil + else + Cons x (make_list x (n - 1)) + +(* Fold implementation with one parameters *) +(*[TODO] Ensures is missing... *) +let rec fold_left (f: 'acc -> 'a -> 'acc) (acc: 'acc) (l: list 'a) : 'acc + variant { l } += match l with + | Nil -> acc + | Cons x xs -> fold_left f (f acc x) xs + end + +(* Fold implementation with two parameters *) +(* [TODO] See fold_product in List*) +let rec fold_left2 (f: 'acc -> 'a -> 'b -> 'acc) + (acc: 'acc) + (l1: list 'a) + (l2: list 'b) : 'acc + requires { length l1 = length l2 } + variant { l1 } + = match l1, l2 with + | Nil, Nil -> acc + | Cons x xs, Cons y ys -> fold_left2 f (f acc x y) xs ys + | _, _ -> absurd + end + +(* Simple map function *) +let rec map (f: 'a -> 'b) (l: list 'a) : list 'b +ensures { length result = length l } +variant { l } += match l with + | Nil -> Nil + | Cons x xs -> Cons (f x) (map f xs) +end + + +(* =========================================================================== *) +(* Simple implementation of a map (using a list) *) +(* Used to associate a value to a tensor *) +(* =========================================================================== *) + +type fmap = list (tensor, option value) + +(* Get an item from the map (spec)*) +(* Logic functions must be total, so we return None if the tensor is not in the map. *) + (* /\ I wuld like to specify this function as if exists k,v . mem (k,v) m then v else None *) +function fget_logic (m: fmap) (k: tensor) : option value = + match m with + | Nil -> None + | Cons (k', v) xs -> if k = k' then v else fget_logic xs k + end + +(* Get an item from the map (imp)*) +let rec fget (m: fmap) (k: tensor) : option value = + requires{ exists v. mem (k,v) m } (* The tensor must be in the map (absurd will never be reached) *) + requires{ forall v1, v2. mem (k,v1) m /\ mem (k,v2) m -> v1=v2} + (* The tensor must be in the map once and only once*) + (* Relaxed : it may appear several times, but with theesam value *) + ensures { result = fget_logic m k} + variant { m } + match m with + | Nil -> absurd + | Cons (k', v) xs -> + if k = k' then v + else fget xs k + end + +(* Set an item in the map (spec) *) +function fset_logic (m: fmap) (k: tensor) (v: option value) : fmap = + match m with + | Nil -> Cons (k, v) Nil + | Cons (k', v') xs -> + if k = k' then Cons (k, v) xs + else Cons (k', v') (fset_logic xs k v) + end + +(* Set an item in the map (imp) *) +let rec fset (m: fmap) (k: tensor) (v: option value) : fmap = + ensures { result = fset_logic m k v } (* The value is correctly set *) + ensures{ forall k', v'. mem (k', v') m /\ k' <> k -> + exists k'', v''. mem (k'',v'') result /\ v'=v''} (* The other values are not modified *) + variant { m } + match m with + | Nil -> Cons (k, v) Nil + | Cons (k', v') tl -> + if k = k' then Cons (k, v) tl + else Cons (k', v') (fset tl k v) + end + +(* If I set (k,v) in the map, then get(k) shall return v *) +lemma get_set_eq: + forall m: fmap, k: tensor, v: option value. + fget_logic (fset_logic m k v) k = v + +(* When I set (k,v) in the map, then the rest of the map must not change, +i.e., for any other key k', the value associated with k' shall not change *) +lemma get_set_neq: + forall m: fmap, k1 k2: tensor, v: option value. + k1 <> k2 -> + fget_logic (fset_logic m k2 v) k1 = fget_logic m k1 + +(* ------------------------------------------------------------------------- *) +(* Link ("collage") between the abstract map and the implementation map *) +(* ------------------------------------------------------------------------- *) + +(* Logical projection from the concrete fmap to the abstract map *) +function fmap_to_map (m: fmap): Map.map tensor (option value) = + fun k -> fget_logic m k + +(* [TODO] The inverse projection is not defined, as the fmap is not a total function *) +(* It is not possible to get a fmap from a map, as the map may contain keys that are not in the fmap *) + +(* Logical projection from the abstract map to the concrete fmap *) +function map_to_fmap (m: Map.map tensor (option value)) : fmap + +(* get for fmap and Map are equivalent *) +lemma fmap_get_complies_with_map_get: + forall m: fmap, k: tensor. + match fget_logic m k with + | Some v -> Map.get (fmap_to_map m) k = Some(v) + | None -> true + end + +(* set for fmap and Map are equivalent *) +lemma fmap_set_complies_with_map_set: + forall m: fmap, k: tensor, v: option value. + fmap_to_map (fset_logic m k v) = Map.set (fmap_to_map m) k v + + +(* =========================================================================== *) +(* Operators *) +(* =========================================================================== *) + +(* Computes the outputs of a node *) +(* Currently, this is a dummy implementation that returns a list of identical values *) +function eval_operator_log (op: operator) (inputs: list (option value)) : list (option value) + +(* Function modeling the behavior of an operator *) +(* [TODO] Replace with actual operators *) +let eval_operator (op: operator) (inputs: list (option value)) : list (option value) +requires { length inputs = length op.opi } (* The node provides as any iputs as needed by the operator *) +requires { forall i. mem i inputs -> i <> None } (* All inputs are initialized before execution *) +ensures { forall i. mem i result -> i <> None } (* All outputs are initialized after execution*) +ensures { length result = length op.opo } (* There is one value per output tensor *) += + (* This is a dummy implementation that returns the appropriate number of identical values *) + make_list (Some 0) (length op.opo) + + +(* ------------------------------------------------------------------------- *) +(* True if a tensor t is initialized in state s. *) +(* ------------------------------------------------------------------------- *) +predicate tensor_is_initialized (s: graph_state) (t: tensor) = + match Map.get s t with + | Some _ -> true + | None -> false +end + +(* ------------------------------------------------------------------------- *) +(* True if a tensor t is initialized in state s. *) +(* ------------------------------------------------------------------------- *) +let is_initialized (s: graph_state) (t: tensor) : bool +ensures { result = tensor_is_initialized s t } += + match Map.get s t with + | Some _ -> true + | None -> false + end + +let rec are_initialized (s: graph_state) (l: list tensor) : bool = +ensures { result = forall t. mem t l -> tensor_is_initialized s t } +variant {l} +match l with +| Nil -> true +| Cons x xs -> (is_initialized s x) && (are_initialized s xs) +end + +(* ------------------------------------------------------------------------- *) +(* True if node op is executable in state s *) +(* ------------------------------------------------------------------------- *) +predicate node_is_ready (s: graph_state) (op: node) = + (* A node is ready if all its inputs tensors are initialized *) + forall t: tensor. + mem t op.oi -> tensor_is_initialized s t + +let node_ready (s: graph_state) (op: node) : bool +ensures { result = true <-> node_is_ready s op } += + are_initialized s op.oi + + +(* =========================================================================== *) +(* Graph execution *) +(* =========================================================================== *) + +(* --------------------------------------------------------------------------- *) +(* Execute one node *) +(* --------------------------------------------------------------------------- *) +let ghost exec_node (s: graph_state) (n: node) : graph_state +requires { node_is_ready s n } (* The node must be ready to be executed *) +requires { length n.oi = length n.ope.opi } (* The number of inputs must match the number of operator's inputs *) +requires { length n.ou = length n.ope.opo } (* The number of outputs must match the number of operator's outputs *) +ensures { forall t: tensor. mem t n.ou -> tensor_is_initialized result t } (* All output tensors are set *) += + (* Function that sets the value of a tensor in the graph state *) + let f (s: graph_state)(t: tensor)(v: (option value) ) : graph_state + (* The value of the tensor is set to v in the state st *) + requires { v <> None } (* We never assign a None value during the execution of a node *) + ensures { tensor_is_initialized result t } + = + fun k -> fget (fset (map_to_fmap s) t v) k + in + (* the values of tensors that are inputs to a node *) + let inputs = map (fun t -> Map.get s t) n.oi in + assert { forall v. mem v inputs -> v <> None }; + (* the values of all outputs after evaluation *) + let outputs = eval_operator n.ope inputs in + assert { forall v. mem v outputs -> v <> None }; + (* the updated state *) + fold_left2 (f) s n.ou outputs + +(* --------------------------------------------------------------------------- *) +(* Execute all nodes. + Returns the graph state after executing all nodes in the graph + The next node to be executed is chosen non deterministically among the + executable nodes *) +(* --------------------------------------------------------------------------- *) + +let rec ghost exec_nodes (s: graph_state) (ns: list node) : graph_state = + requires { forall n. mem n ns -> node_is_ready s n } (* All nodes in the list are ready to be executed *) + ensures { forall n : node. mem n ns -> + forall t: tensor. mem t n.ou -> + tensor_is_initialized result t } (* All outputs of the nodes are initialized *) + variant { ns } + match ns with + | Nil -> s + | Cons n ns -> + let s' = exec_node s n in + exec_nodes s' ns + end + + +let rec filter ( b: node -> bool) (l: list node) : list node = + ensures { forall n. mem n result <-> (mem n l /\ b n) } (* The result contains only nodes that satisfy the predicate b *) + variant {l} + match l with + | Nil -> Nil + | Cons x xs -> + if b x then Cons x (filter b xs) + else filter b xs + end + + +let rec ghost exec_nodes_until_completion (s: graph_state) (ns: list node) : graph_state = + (* All the output tensors of the nodes in ns are initialized *) + requires { forall n. mem n ns -> node_is_ready s n } (* All nodes in ns are ready *) + ensures { forall n: node. mem n ns -> + forall t: tensor. mem t n.ou -> + tensor_is_initialized result t } (* All outputs of the nodes are initialized *) + variant {ns} + let en = filter (fun n -> node_ready s n) ns + in + let dn = filter (fun n -> not node_ready s n) ns + in + match en with + | Nil -> s (* No node is ready, return the current state *) + | Cons n _ -> + let s' = exec_node s n in + exec_nodes_until_completion s' dn (* Execute the node and continue with the remaining nodes *) + end + +let ghost exec_graph (s: graph_state) (g: graph) : graph_state + requires { forall t. mem t g.gi -> tensor_is_initialized s t } (* The graph can only be executed if its inputs are initialized *) + ensures { forall t. mem t g.go -> tensor_is_initialized result t } (* After execution, all outputs are initialized *) + = + exec_nodes_until_completion s g.gn + + +(* ------------------------------------------------------------------------- *) +(* Initial tensor state: all tensors are undefined *) +(* ------------------------------------------------------------------------- *) +let ghost set_initial_state (g: graph) : graph_state = + fold_left (fun s t -> Map.set s t None) (Const.const None) g.gt + + +(* ------------------------------------------------------------------------- *) +(* /\ !Simple test: a fold-left and on list l is true iff all elements of + l are true. Why does the proof fail? *) +(* ------------------------------------------------------------------------- *) +predicate all_true (l: list bool) = + forall b: bool. mem b l -> b = true + +lemma and_true_true: + forall a: bool, b: bool. Bool.andb a b <-> a /\ b + +goal fold_left_and_equiv_all_true: + forall l: list bool. + FoldLeft.fold_left Bool.andb true l = true <-> all_true l +(* ------------------------------------------------------------------------- *) + + +end + + + diff --git a/safety-related-profile/documents/profile_graph/graph.md b/safety-related-profile/documents/profile_graph/graph.md index 6a1a0eda..7987c3fe 100644 --- a/safety-related-profile/documents/profile_graph/graph.md +++ b/safety-related-profile/documents/profile_graph/graph.md @@ -1 +1,556 @@ -(Specification of the graph semantics and constraints. *To be completed*) \ No newline at end of file +## Restrictions +The following restrictions apply to graphs in the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `[R1]`| There shall be a 1-to-1 mapping between the inputs and outputs of a node and the inputs and outputs of its associated operator. | TBC | +| `[R2]` | All computation nodes of a graph must contribute to the computation of the graph outputs | No dead computation nodes | +| `[R3]` | A graph must only contain computation nodes referring to operators in the SONNX subset | TBC | + +> (eric) Add a reference to ONNX documentation, e.g., [ONNX concepts](https://onnx.ai/onnx/intro/concepts.html). + +# Informal specification + +## Definitions +### Graph +- `[T01a]` A graph is an acyclic graph composed of *nodes* and *edges* +- `[T01b]` A node is either a *tensor node* or a *computation node* + +> The fact that the graph is acyclic may not be necessary since there is also the SSA constraint... + +In the following example[^1], written using the `onnxruntime` API, the graph is composed of four computation nodes (`add_node`, `cons_node`, `mul_node1`, `mul_node2`) and 4 tensors nodes (`g_i1` and `g_i2`, `op1_o`, `op3_o`). The complete example in given in Section [example](#example) below). + +```python +# Create nodes +add_node = onnx.helper.make_node(op_type="Add", inputs=[g_i1_id, g_i2_id], outputs=[op1_o_id]) +cons_node = onnx.helper.make_node("Constant", inputs=[], outputs=[op2_o_id], value=const_value) +mul_node1 = onnx.helper.make_node("Mul", inputs=[op1_o_id, op2_o_id], outputs=[op3_o_id]) +mul_node2 = onnx.helper.make_node("Mul", inputs=[g_i1_id, g_i2_id], outputs=[op4_o_id]) +[...] +# Create graph +graph = onnx.helper.make_graph( + nodes=[add_node, cons_node, mul_node1, mul_node2], + name="Example", + inputs=[g_i1, g_i2], + outputs=[op1_o, op3_o] +) +[...] +``` +[^1]: The complete example in given in Section [example](#example) below. + + +### Tensor nodes +- `[T02a]` A tensor node is an object that can have a value or no value +- `[T02b]` A tensor node is identified by a unique identifier within a graph + +`[RX]` A tensor node that is not the input of a computation node (i.e., it does not belong to any edges to a computation node input) must be an output node of the graph. + +In the following example[^1], written using the `onnxruntime` API, a tensor node is created using the `onnx.helper.make_tensor_value_info` function. In this example, `op1_o` is a tensor of rank 2 with no given dimensions. Its identifier is `op1_o` is `OP1_O`. + +```python +op1_o_id = "OP1_O" +op1_o = onnx.helper.make_tensor_value_info(op1_o_id, onnx.TensorProto.FLOAT, [None, None]) +``` + +### Data flow computation nodes + +- `[T03a]` A computation node specifies some relation between its inputs and its outputs. +- `[T03b]` The relation is defined by the *operator* that is associated with the node. The semantics of operators is defined in the SONNX profile opset (see, e.g., [add](../profile_opset/add/add.md)). + - Note that multiple computation nodes can refer to the same operator. +- `[T03c]` There is a 1-to-1 mapping between the computation node's inputs and outputs and those of its associated operator . + +- `[RX]` A computation node must belong to at least one path from an input tensor to an output tensor of the graph (i.e., there shall be no "dead" node) +- `[Rx]` A computation node must refer to an operator in the SONNX profile. + +In the following example[^1], nodes `mul_node1` and `mul_node2` refer to the same operator [`Mul`](../profile_opset/mul/mul.md) that has 2 inputs and 1 output. + +```python +mul_node1 = onnx.helper.make_node("Mul", [op1_o_id, op2_o_id], [op3_o_id]) +mul_node2 = onnx.helper.make_node("Mul", [g_i1_id, g_i2_id], [op4_o_id]) +``` + +### Control flow computation nodes + +> See later. + + +### Edges +- `[T04a]` An edge is a relation between a computation node, one input or output of this computation node, and a tensor node. A tensor that is related to some computation node input (resp. output) is said to be an input (resp. output) of the computation node. + +- `[Rx]` All inputs and outputs of all computation nodes must belong to an edge. + - Note that is is a restriction with respect to the ONNX standard that allows fewer inputs or outputs when the omitted input or output is optional + +In the following example[^1], edges are defined by the `inputs` and `outputs` arguments of the `make_node` and `make_graph` functions. For instance, the `add_node` computation node has 2 inputs (one for each element of the `inputs` list, noted $i_1$, $i_2$ hereafter) and 1 output (one for each element of the `outputs` list, noted $o$). The corresponding graph edges are: +- e1: (add_node, $i_1$, g_i1_id) +- e2: (add_node, $i_2$, g_i2_id) + +```python +# Create nodes +add_node = onnx.helper.make_node(op_type="Add", inputs=[g_i1_id, g_i2_id], outputs=[op1_o_id]) +cons_node = onnx.helper.make_node("Constant", inputs=[], outputs=[op2_o_id], value=const_value) +mul_node1 = onnx.helper.make_node("Mul", inputs=[op1_o_id, op2_o_id], outputs=[op3_o_id]) +mul_node2 = onnx.helper.make_node("Mul", inputs=[g_i1_id, g_i2_id], outputs=[op4_o_id]) +[...] +# Create graph +graph = onnx.helper.make_graph( + nodes=[add_node, cons_node, mul_node1, mul_node2], + name="Example", + inputs=[g_i1, g_i2], + outputs=[op1_o, op3_o] +) +[...] +``` + +### Operators +- `[T05a]` An operator specifies a function, i.e., a relation between the values of some input variables (the arguments of the function) and some output variables. For instance operation $z=\text{\bf add}(x,y)$ specifies a relation between variables $x$, $y$, and $z$ such that $z=x+y$. + - The set of input variables may be empty (case of a constant function). + + +> (eric) Can a node have an internal state? In that case, it is not functional. + +### Example +The following figure gives an example of a simple graph composed of 4 nodes. The graph has 2 input and 2 output tensors. Note that output of one of the nodes, `mul_node2`, is not used: this is actually forbidden in SONNX. + +![alt text](imgs/ex1_netron.png) + +The `onnxruntime`used to create the graph is given hereafter: + +```python +import onnx +import onnxruntime as ort +import numpy as np + +# Define tensor identifiers +g_i1_id = "G_I1" +g_i2_id = "G_I2" +op1_o_id = "OP1_O" +op2_o_id = "OP2_O" +op3_o_id = "OP3_O" +op4_o_id = "OP4_O" + +# Create 2-rank input tensors (two inputs for division) +g_i1 = onnx.helper.make_tensor_value_info(g_i1_id, onnx.TensorProto.FLOAT, [None, None]) +g_i2 = onnx.helper.make_tensor_value_info(g_i2_id, onnx.TensorProto.FLOAT, [None, None]) + +# Create output tensors +op1_o = onnx.helper.make_tensor_value_info(op1_o_id, onnx.TensorProto.FLOAT, [None, None]) +op2_o = onnx.helper.make_tensor_value_info(op2_o_id, onnx.TensorProto.FLOAT, [None, None]) +op3_o = onnx.helper.make_tensor_value_info(op3_o_id, onnx.TensorProto.FLOAT, [None, None]) +op4_o = onnx.helper.make_tensor_value_info(op4_o_id, onnx.TensorProto.FLOAT, [None, None]) + +# Create a constant value +const_value = onnx.helper.make_tensor( + name='const_tensor', + data_type=onnx.TensorProto.FLOAT, + dims=[2, 2], + vals=np.array([[1.0, 2.0], [3.0, 4.0]], dtype=np.float32).flatten() # Example values, adjust as needed +) + +# Create nodes +add_node = onnx.helper.make_node("Add", [g_i1_id, g_i2_id], [op1_o_id]) +cons_node = onnx.helper.make_node("Constant", [], [op2_o_id], value=const_value) +mul_node1 = onnx.helper.make_node("Mul", [op1_o_id, op2_o_id], [op3_o_id]) +mul_node2 = onnx.helper.make_node("Mul", [g_i1_id, g_i2_id], [op4_o_id]) + +# Create the ONNX graph +graph = onnx.helper.make_graph( + nodes=[add_node, cons_node, mul_node1, mul_node2], + name="Example", + inputs=[g_i1, g_i2], + outputs=[op1_o, op3_o] +) + +# Create the ONNX model +model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid("", 13)], ir_version=10) # Explicitly set opset to 13 and ir_version to 10 +onnx.checker.check_model(model) +print(onnx.helper.printable_graph(model.graph)) + +# Save the model +onnx.save(model, "graph.onnx") + +# Load and run the model using ONNX Runtime +session = ort.InferenceSession("graph.onnx") + +# Do inference +i1 = np.array([[1.0, 2.0],[3.0, 4.0]], dtype=np.float32) +i2 = np.array([[3.0, 4.0],[5.0, 6.0]], dtype=np.float32) +output = session.run(None, {g_i1_id: i1, g_i2_id: i2}) + +# Display results +i1_f=(np.array2string(i1, separator=',', max_line_width=np.inf).replace('\n', '')) +i2_f=(np.array2string(i2, separator=',', max_line_width=np.inf).replace('\n', '')) +o1_f=(np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\n', '')) +o2_f=(np.array2string(output[1], separator=',', max_line_width=np.inf).replace('\n', '')) + +# Display results +np.set_printoptions(precision=None, floatmode='fixed') +print(f"I1={i1_f}, I2={i2_f}") +print(f"Result={o1_f}{o2_f}") +``` + +## Execution Semantics +Executing a graph means evaluating the output tensors of the graph according to the following rules: +- `[T06a]` A computation node is executable if all the tensors connected to its inputs (i.e., belonging to an edge) are initialized + +> Only valid for data flow graphs. To be completed to account for control flow nodes. + +- `[T06b]` Executing a computation node means assigning values to the tensors connected to its outputs (i.e., belonging to an edge) so that the relation specified by the operator between its inputs and outputs holds +- `[T06c]` All executable computation nodes shall be executed +- `[T06e]` A tensor shall be assigned a value at most once (Single Assignment) + + +## Special nodes + +ONNX provides two other categories of special nodes: +- function nodes +- control flow nodes. + +*This section is very preliminary* + +The way graphs are described and executed is independent from the operators used in the graph. In other terms, the semantics of the graph (how the operators are called) and the semantics of the operators (what the operators do) are defined separately. This modularity applies to the standard operators (convolution, relu, etc.) and to the special function and control flow nodes. + +### Functions nodes + +A "function" node is defined using primitive ONNX operators or other function operators. Function nodes are introduced [here](https://onnx.ai/onnx/intro/concepts.html), Section "Functions". + +Some ONNX operators are defined as functions, e.g.: +- Softplus +- Softsign +- - Celu +- HardSwish +- Gelu +- Selu + + +For instance, the `SoftwaPlus`operator is defined as follows: +``` +< + domain: "", + opset_import: ["" : 18] +> +Softplus (X) => (Y) +{ + exp_x = Exp (X) + one = Constant () + one_cast = CastLike (one, X) + exp_x_add_one = Add (exp_x, one_cast) + Y = Log (exp_x_add_one) +} +``` + +which is a readable description directly generated from the implementation model which is located in file [onnx/defs/math/defs.cc](https://github.com/onnx/onnx/blob/main/onnx/defs/math/defs.cc) + +A function operator has a `.FunctionBody()` call that describes the subgraph: + +``` +ONNX_OPERATOR_SET_SCHEMA( + Softplus, + 22, + OpSchema() + .SetDoc(Softplus_ver22_doc) + .Input(0, "X", "Input tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable) + .Output(0, "Y", "Output tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable) + .TypeConstraint("T", OpSchema::all_float_types_ir4(), "Constrain input and output types to float tensors.") + .TypeAndShapeInferenceFunction(propagateShapeAndTypeFromFirstInput) + .FunctionBody( + R"ONNX( + { + exp_x = Exp (X) + one = Constant () + one_cast = CastLike (one, X) + exp_x_add_one = Add (exp_x, one_cast) + Y = Log (exp_x_add_one) + } + )ONNX", + 18)); +``` + +The function body can be obtained using the Python API: +```Python +schema = defs.get_schema("Softplus", domain="", max_inclusive_version=21) +fproto = schema.function_body +print(str(fproto)) +``` + +that returns: +``` +name: "Softplus" +input: "X" +output: "Y" +node { + input: "X" + output: "exp_x" + op_type: "Exp" + domain: "" +} +node { + output: "one" + op_type: "Constant" + attribute { + name: "value" + t { + data_type: 1 + float_data: 1 + name: "" + } + type: TENSOR + } + domain: "" +} +node { + input: "one" + input: "X" + output: "one_cast" + op_type: "CastLike" + domain: "" +} +node { + input: "exp_x" + input: "one_cast" + output: "exp_x_add_one" + op_type: "Add" + domain: "" +} +node { + input: "exp_x_add_one" + output: "Y" + op_type: "Log" + domain: "" +} +doc_string: "\nSoftplus takes one input data (Tensor) and produces one output data\n(Tensor) where the softplus function, y = ln(exp(x) + 1), is applied to\nthe tensor elementwise.\n" +opset_import { + domain: "" + version: 18 +} +domain: "" +``` + +Only function operators have the `.FunctionBody` description. For instance, for the `Abs` operator, we find, in the source tree: + +``` +ONNX_OPERATOR_SET_SCHEMA( + Abs, + 13, + OpSchema() + .SetDoc(Abs_ver13_doc) + .Input(0, "X", "Input tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable) + .Output(0, "Y", "Output tensor", "T", OpSchema::Single, true, 1, OpSchema::Differentiable) + .TypeConstraint( + "T", + OpSchema::all_numeric_types_ir4(), + "Constrain input and output types to all numeric tensors.") + .TypeAndShapeInferenceFunction(propagateShapeAndTypeFromFirstInput)); +``` + +To check whether an operator is primitive or not, one can use the ONNX `get_schema()`function: +``` +from onnx import defs +schema = defs.get_schema("Softmax", domain="", max_inclusive_version=21) +print(schema.has_function) +schema = defs.get_schema("Softplus", domain="", max_inclusive_version=21) +print(schema.has_function) +False +True +`` + +The complete list of function operators can be optained using: +```Python +from onnx import defs +function_ops = [ + s.name for s in defs.get_all_schemas_with_history() if s.has_function +] +print(function_ops) +``` + + + +The representation of functions is models defined in[onnx.proto](https://github.com/onnx/onnx/blob/main/onnx/onnx.proto), item `message Functionproto`. + + +- A `function` operator encapsulates a graph. +- Executing a function operator means executing the encapsulated graph according to the graph execution semantics described before. +- An encapsulated graph may itself use `function` nodes, in a hierarchical manner. +- A valid ONNX graph with function nodes must be always actually converted to an equivalent ONNX graph without any function nodes. This forbids any direct or indirect recursion. + +### Control-flow operators +- ONNX provides three control flow operators: + - [If](https://onnx.ai/onnx/operators/onnx__If.html) + - [Loop](https://onnx.ai/onnx/operators/onnx__Loop.html) + - [Scan](https://onnx.ai/onnx/operators/onnx__Scan.html) + +As stated in the [documentation]() The structures are slow and complex. It is better to avoid them if possible". We define their semantics for completeness, but the SONNX profile does not contain any control flow operator. + +Those nodes take either one (e.g, operators `loop`, `scan`,...) or two graphs (`if`) as attributes and execute them graphs according to their specific semantics. + +The `if` operator is functional: each tensor is at most assigned once (SSA). + +The `loop`or `scan` operators are functional: each tensor is assigned at most once, each iteration producing a different tensor. Semantically, a `loop` or `scan` operator can be replaced by its unrolled version (looping $N$ times over graph $g$ on input $x$ is conceptually equivalent to computing $g \circ g \circ ... \circ (x)$. +``` +f_body : (iter, cond, state) → (cond', state') +Loop(N, True, state0) + = let (cond1, state1) = f_body(0, True, state0) + (cond2, state2) = f_body(1, cond1, state1) + ... + (condN, stateN) = f_body(N-1, condN-1, stateN-1) + in stateN +``` + +#### The `loop` operator + +- A `loop` operator executes some graph (`body`) according to some condition whihc can be a number of iterations or a certain condition. +> The graph run each iteration. It has 2+N inputs: (iteration_num, condition, loop carried dependencies…). It has 1+N+K outputs: (condition, loop carried dependencies…, scan_outputs…). Each scan_output is created by concatenating the value of the specified output value at the end of each iteration of the loop. It is an error if the dimensions or data type of these scan_outputs change across loop iterations. + + + +#### The `if` operator + +- An `if` operator, takes one boolean input and two attributes (`then_branch` and `else_branch`). When the input is true, the graph designated by the `then_branch` is executed, otherwise, the graph designed by the `else_branch`is executed. +- The `if` operator has one variadic output that is composed of the outputs of the two sub-graphs. +- As for any other operator, the `if` node has a single list of outputs (e.g., `[Y1, Y2, ..., Yn]`). + - The `then_branch` and the `else_branch` subgraphs must produce the same number and types of outputs, and those are mapped to the outputs of the `if` node. + - For a given output, the shape of the corresponding output tensor of the then and else branches can be different, but the shape of the `if` outputs must be compatible with both of them. +- The then and else subgraphs have their own input tensors. Only the subgraph that is selected by the boolean input is executed. This means that if the tensors of the + + +> This means that in the definition of the graph semantics, some execution nodes may ot be executed. + +``` +import onnx +from onnx import helper, TensorProto, numpy_helper + +cond_input = helper.make_tensor_value_info("cond", TensorProto.BOOL, []) + +if_output = helper.make_tensor_value_info("if_out", TensorProto.FLOAT, []) + +# Create a constant node for the then branch +then_const = helper.make_node("Constant", [], ["then_out"], + value=helper.make_tensor(name="const_tensor_1", data_type=TensorProto.FLOAT, dims=[], vals=[1.0]) +) +then_output = helper.make_tensor_value_info("then_out", TensorProto.FLOAT, []) + +# The "then" subgraph contains a unique "constant" node. +then_branch = helper.make_graph([then_const], "then_branch", [], [then_output]) + +# Create a constant node for the else branch +else_const = helper.make_node("Constant", [], ["else_out"], + value=helper.make_tensor(name="const_tensor_0", data_type=TensorProto.FLOAT, dims=[], vals=[0.0]) +) +else_output = helper.make_tensor_value_info("else_out", TensorProto.FLOAT, []) + +# The "else" subgraph contains a unique "constant" node. +else_branch = helper.make_graph([else_const], "else_branch", [], [else_output]) + +# Create the "if" node that contains the "then" and "else" subgraphs. +if_node = helper.make_node( + "If", + inputs=["cond"], + outputs=["if_out"], + then_branch=then_branch, + else_branch=else_branch +) + +# Ceate the toplevel graph. +graph = helper.make_graph( + nodes=[if_node], + name="if_example", + inputs=[cond_input], + outputs=[if_output] +) + +model = helper.make_model(graph, producer_name="onnx-if-minimal") +onnx.checker.check_model(model) +onnx.save(model, "if_example.onnx") + +``` + +- In addition, they may use tensors directly available in the scope of the `if` operator. In the following example, the `then_branch` and the `else_branch` subgraphs "capture" tensor `X` that is declared in the top-level graph. + + +> **DO WE ACCEPT SUCH CAPTURE OR DO WE IMPOSE ALL FLOWS TO BE DECLARED IN THE IF NODE**? + +``` +import onnx +from onnx import helper, TensorProto + + +cond_input = helper.make_tensor_value_info("cond", TensorProto.BOOL, []) + +# X is an input tensor of the graph +x_input = helper.make_tensor_value_info("X", TensorProto.FLOAT, [1]) + +if_output = helper.make_tensor_value_info("if_out", TensorProto.FLOAT, [1]) + +# Create constant node with value 2 +two_const = helper.make_node( + "Constant", + inputs=[], + outputs=["two"], + value=helper.make_tensor("two_val", TensorProto.FLOAT, [1], [2.0]) +) + +# Create node X*2 +mul_node = helper.make_node("Mul", ["X", "two"], ["then_out"]) +then_output = helper.make_tensor_value_info("then_out", TensorProto.FLOAT, [1]) +then_branch = helper.make_graph( + [two_const, mul_node], + "then_branch", + inputs=[], + outputs=[then_output] +) + +# Create constant node with value 3 +three_const = helper.make_node( + "Constant", + inputs=[], + outputs=["three"], + value=helper.make_tensor("three_val", TensorProto.FLOAT, [1], [3.0]) +) + +# Create node X+3 +add_node = helper.make_node("Add", ["X", "three"], ["else_out"]) +else_output = helper.make_tensor_value_info("else_out", TensorProto.FLOAT, [1]) +else_branch = helper.make_graph( + [three_const, add_node], + "else_branch", + inputs=[], + outputs=[else_output] +) + +# Create IF node with no input (but the condition) +if_node = helper.make_node( + "If", + inputs=["cond"], + outputs=["if_out"], + then_branch=then_branch, + else_branch=else_branch +) + +# Create the main graph +main_graph = helper.make_graph( + [if_node], + "if_capture_outer_scope", + inputs=[cond_input, x_input], + outputs=[if_output] +) + +# Model +model = helper.make_model(main_graph, producer_name="onnx-if-outer-scope") +onnx.checker.check_model(model) +onnx.save(model, "if_capture_outer_scope.onnx") +``` + + +## Additional remarks + +### Properties of a graph +- If all operators are purely functional (stateless), a graph is also purely functional, i.e., the values of its outputs only depends on the values of its inputs and the values of the attributes of its nodes. +- If all operators are deterministic, a graph is also deterministic, i.e., for a given set of input values, the execution of the graph always gives the same output values. +- A graph has no side effect, i.e., the only visible effects of a graph are via its outputs. + + + + + +# Formal specification + +See draft [here](../profile_formal/onnxgraph.mlw). diff --git a/safety-related-profile/documents/profile_graph/imgs/div.onnx b/safety-related-profile/documents/profile_graph/imgs/div.onnx new file mode 100644 index 00000000..8d5ef93a Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/div.onnx differ diff --git a/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png b/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png new file mode 100644 index 00000000..2d3324eb Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/ex1_netron.png differ diff --git a/safety-related-profile/documents/profile_graph/imgs/graph.png b/safety-related-profile/documents/profile_graph/imgs/graph.png new file mode 100644 index 00000000..6295388a Binary files /dev/null and b/safety-related-profile/documents/profile_graph/imgs/graph.png differ diff --git a/safety-related-profile/documents/profile_graph/reviews/README.md b/safety-related-profile/documents/profile_graph/reviews/README.md new file mode 100644 index 00000000..800d6736 --- /dev/null +++ b/safety-related-profile/documents/profile_graph/reviews/README.md @@ -0,0 +1 @@ +Review of the "graph" specification. \ No newline at end of file diff --git a/safety-related-profile/documents/profile_graph/reviews/edoardo.md b/safety-related-profile/documents/profile_graph/reviews/edoardo.md new file mode 100644 index 00000000..2b0554ad --- /dev/null +++ b/safety-related-profile/documents/profile_graph/reviews/edoardo.md @@ -0,0 +1,176 @@ +> **_NOTE:_** Copy of "graph.md" with notes by Edoardo Manino + +# Preamble + +This document gives a high level description of how the execution execution of an ONNX graph. How the graph graph elements are described in the ONNX file is given in the Intermediate Representation (IR) specification document. + +In the context of SONNX, the specification of the semantics of an ONNX graph is limited to the inference phase. + +> **_NOTE 1:_** Typos: "execution" and "graph" are repeated twice. The first sentence is broken: maybe it is meant to say "how the execution of an ONNX graph _takes place_"? + +> **_REPLY 1:_** OK: Corrected. + +# Informal specification + +> **_NOTE 2:_** The document does not contain a _formal_ specification (yet?). If it exists in another document, mention that. Also, it may be worth having a little table of content listing the main sections: i.e. structure, behaviour, special nodes, additional remarks. + +> **_REPLY 2:_** OK: There is no formal specification yet. As for the operators, I add a "Formal specification" section that will point to the Why3 spec (which does not exist yet). The document does not contain a _formal_ specification (yet?). If it exists in another document, mention that. Also, it may be worth having a little table of content listing the main sections: i.e. structure, behaviour, special nodes, additional remarks. + + +## Structure + +### Graph +- In ONNX, a **model** is represented by a **graph**. Evaluating a model means evaluating the graph. +- A **graph** + - is composed of a set of **nodes** and **edges** + - has zero or more inputs and at least one output. + +> **_NOTE 3:_** Later we state that nodes in the graph may have unused outputs. I can see how that could be helpful, e.g. use a complicated operator for just a few of its outputs, or pad a computational graph with irrelevant operators to avoid side-channel attacks. However, we are losing the nice recursive property that any subgraph of an ONNX graph is also a valid ONNX graph, because an arbitrary subgraph _may not have any outputs_. For an example, see the illustration below: the subgraph containing only the "Sub" node is not a valid ONNX graph. Do we care about this recursive definition? Should we change the definition of ONNX graphs to include graphs without any outputs? + +> **_REPLY 3:_** This has to be discussed... One point : a graph is a particular object... In particular, the output of a graph may be "driven" by outputs of multiple node, but outputs of different nodes cannot be connected together (due to the SSA constraint). + + +- An **input** or an **output** is either undefined or has a value. + +> **_NOTE 4:_** Maybe it is worth clarifying the last sentence. By "undefined" do we mean we have not computed its value yet, its value does not matter, or that its value is non-deterministic? When an input/output has a value, can the value change during this specific execution or is it constant once it is computed? + +> **_REPLY 4:_** KO: I don't see clearly the ambiguity. In computer science, "undefined" generally (often?) means "not initialized", "not assigned". I don't like the term "initialized" because it refers to a specific phase of "initialization". "Not assigned" would be fine. I have changed to : "An **input** or an **output** is either defined (i.e. it has a value) or undefined (it has has no value)." + +- A **node** + - refers to a fully qualified and configured ONNX **operator** (the version of the operator is defined, the attributes of the operators are set). + - has inputs and outputs corresponding to the inputs and outputs of the referenced operator. +- An **edge** + - is a connection between an input and an output of two different nodes, or a connection between an input (resp. output) of the graph and an input (resp. output) of a node so that, + +> **_NOTE 4:_** I would complete the sentence with "...so that the following constraints hold." and rename the next section as "Graph Constraints". + +> **_REPLY 5:_** OK: Agree, but I keep the section title ("constraints") to be consistent with the operators specification. + +> ** NOTE (Eric) ** In fact, I am not convinced that the use of nodes and edges is a good idea. A better model would be that nodes have inputs and output consuming and producing tensors. Connections between nodes would be done by referring to the same tensor. Thi sis how I have started modeling formally a graph. + +### Constraints +- (C1) Each input of the graph is connected to one or more inputs of its nodes. +- (C2) Each output of the graph is connected to the output of one of its nodes. +- (C3) A graph is acyclic. + +> **_NOTE 6:_** Should we mention that the graph is also directed? We clearly imply it, since all edges connect the output of a node to the input of another (C5-6). + +> **_REPLY 6:_** KO: I don't think that we have to say that it is directed because, as you write, this is pretty clear from the existence of input and outputs and the way there are connected. + +- (C4) A node has zero or more inputs and at least one output. +- (C5) A node's input is either connected to the output of another node or to an input of the graph. +- (C6) A node's output is either connected to the input of another node, to an output of the graph, or remains unconnected. + +> **_NOTE 7:_** Typo: "node" is inanimate so we cannot use the Saxon genitive. Say "The input/output of a node" instead of "A node's input/output". + +> **_REPLY 7:_** OK: Corrected, even though the rule you mentioned is not really applied any more... See e.g., [here](https://english.stackexchange.com/questions/1031/is-using-the-possessive-apostrophe-correct-in-the-cars-antenna/100073#100073). + +### Illustration +The following picture gives a simple example of a graph composed of 4 nodes. In this example, the inputs of the graph are connected to the inputs of two nodes (`add` and `sub`) and the output of the `sub` node is not used. + +

+ +

+ +Here is a textual export of same model using `onnx.helper.printable_graph(model.graph)`. + +``` +graph Test ( + %I1[FLOAT, ?x?] + %I2[FLOAT, ?x?] +) { + %O1 = Add(%I1, %I2) + %op2_out = Constant[value = ]() + %O2 = Mul(%O1, %op2_out) + %op4_out = Sub(%I1, %I2) + return %O1, %O2 +} +``` + +*(Note that ONNX also defines another textual serialization scheme (only available in C++)* + +## Behaviour +- Evaluating a graph's output requires evaluating the output of the node it is connected to. +- Evaluating a node's output is done by executing the node. + +> **_NOTE 8:_** Typos: "Evaluating an output of a graph/node" +> **_REPLY 8:_** OK. Corrected. (but see my previous reply) + +- Executing a node means computing its outputs based on the specification of the referenced operator. +- A node can only be executed if all its input values are defined. +- Initially, all input values are defined + +> **_NOTE 9:_** I presume we are referring to the graph inputs, rather than all node inputs? I would also say "before execution", as it is more precise than "initially". + +> **_REPLY 9:_** (a) KO : but there is an error: "all inputs are undefined." (b) OK + +- Initially, all output values are undefined +- All inputs and outputs connected by an edge are either both undefined or have the same value. + + +## Special nodes + +### Functions nodes + +> **_NOTE 10:_** In this section we are using "encapsulate" and "embed" as synonym. I would pick only one for consistency. +> **_REPLY 10:_** OK : use "encapsulate" +> +- A `function` operator encapsulates a graph. +- Executing a function operator means executing the embedded graph according to the graph execution semantics described before. +- An embedded graph may itself use `function` nodes, in a hierarchical manner. + +> **_NOTE 11:_** We are not saying explicitly whether the same function definition can be reused (called) in more than one node of the graph. +> **_REPLY 11:_** As we are not saying that this is forbidden, it is authorized... I wouldn't say anything more. + +- In ONNX, a `function` node is conceptually expanded ("inlined") at the place where it is used. This forbids any direct or indirect recursion (incl. infinite recursion). + +> **_NOTE 12:_** (a) The last requirement is crucial, but I feel it is expressed in a potentially ambiguous way. Possible alternative: "A valid ONNX graph with function nodes must be always convertible to an equivalent ONNX graph without any function nodes. This forbids any direct..." (b) On this note, we might need to say explicitly (in the graph definition) that the number of nodes and edges must be finite. + +> **_REPLY 12:_** OK. (a) OK : modified as proposed (b) KO: as we are talking about an actual artefact (an ONNX model), I think that this is not necessary. + +### Control-flow operators +- ONNX provides a series of control flow operators such as `if`, `scan`, `loop`,...). +- Those nodes take one (e.g, operators `for`, `loop`, `scan`,...) or two graphs (`if`) as attributes and execute this graph or those graphs according to their specific semantics. +- An `if` node, for instance, takes one boolean input and two attributes, one specifying the graph to be executed when the boolean input is true (the `then_branch`) and another graph when the boolean is false (the `else_branch`). + - Note that one of the graph is not executed. This seems to contradict the execution semantics of a graph bit but is not since executing the `then_branch` or the `else_branch` concerns the semantics of the `if` node, not of the graph. From the graph's perspective, the only node that is visible is the `if`. + + > **_NOTE 13:_** Hmmm, this paragraph sounds a little defensive. Is this disclaimer really necessary? In section "Behaviour", we do not specify whether all outputs should become defined after execution. Thus, we already allow arbitrary sub-graphs to remain non-executed. Am I missing the point here? + + > **_REPLAY 13:_** OK. I remove this sentence. I have added that all outputs of the graph must be defined after execution otherwise the graph is considered to be malformed. Let's say that we have a graph with output O and has a simple IF node where the THEN branch produces O and the ELSE branch produces nothing. Tis graph is invalid. + + - The same applies for the other control flow nodes. + + +## Additional remarks + +### Properties of a graph +- If all operators are purely functional (stateless), a graph is also purely functional, i.e., the values of its outputs only only depends on the values of its inputs and the values of the attributes of its nodes. + +> **_NOTE 14:_** Typo: "only only" is repeated. +> **_REPLY 14:_** OK. + +- If all operators are deterministic, a graph is also deterministic, i.e., for a given set of input values, the execution of the graph always gives the same output values. +- A graph has no side effect, i.e., the only visible effects of a graph are via its outputs. + +> **_NOTE 15:_** Does the latter requirement mean that all operators _must_ be stateless and deterministic? The former two requirements begin with "if", which makes it sound like an option. +> **_REPLY 15:_** TBC: not sure to understand your comment. +> +Note: +- The values of the outputs do not depend on the execution order of its nodes are executed. + +> **_NOTE 16:_** Typo: "execution order ... are executed". +> **_REPLY 16:_** OK. + +- By construction, a graph makes it explicit the order according to which terms of expressions are computed. For instance, expression `a+b+c` is either represented by `(a+b)+c`or `(a+(b+c)` + +> **_NOTE 17:_** This requirement contradicts the previous one. The latter seems to forbid non-determinism in the execution order, the former seems to allow it as long as the output stays the same. I would prefer keeping only one of them, with strong preference towards fixing the execution order. +> **_REPLY 17:_** OK. May sentence was really unclear! What I means is that in ONNX, `a+b+c` is explicitly represented by + `(a+b)+c`or `(a+(b+c)`. + +## Restrictions +The following restrictions apply to graphs in the SONNX profile: +- `[R1]` A graph shall not contain nodes with no connected outputs. + - Rationale: each node of the graph shall contribute to the function of the graph (no "dead node"). + +> **_NOTE 18:_** Shall we change the illustration then? It contains a node "Sub" with a dangling output. Also, clarify whether I can have a node with multiple outputs, some connected, some disconnected. I think R1 allows it, but another reader may not. +> **_REPLY 18:_** TBC. The illustration describes the general case. May be shoud I add a comment stating that this kind of graph is forbidden by R1. The question is : is R1 mandatory? \ No newline at end of file diff --git a/safety-related-profile/documents/profile_graph/reviews/jlf b/safety-related-profile/documents/profile_graph/reviews/jlf new file mode 100644 index 00000000..3458c3d2 --- /dev/null +++ b/safety-related-profile/documents/profile_graph/reviews/jlf @@ -0,0 +1,92 @@ +[Answers from EJN] + +> I think that inputs and outputs shall be defined as elements of the graph. + +OK.Yes. Especially since we have (c1) saying that "A graph has zero or more inputs and at least one output."... +However, I also think that we have to keep things simple since the concept of "input" and "output" is pretty intuitive. + +=> I propose to remove C1 and update the definition of a graph to introduce the concept of input/output + +> Edge: "the input" <-- "a specific input", idem for "output" and idem for the "graph". + +KO: Not convinced that it makes the definition clearer.\ +=> No modification + + +> May be the concept of "port" could be used. + +??: I don't want to make the definition more complicated than needed, as far as +there is no ambiguity for a "typical" reader. + +The concept of "port" would allow introducing the concept of "port delegation" +that would make the connection between graph inputs (resp. outputs) +and nodes inputs ( resp. outputs) cleaner (but not necessarily clearer). + + +> The illustration highlights ports and a hierarchical graph structure +> (one node includes the four nodes). + +OK: Yes, definitely. +The squares in the diagram are actually "ports".\ +=> No modification. See my previous remark. + + +> (C1): Why zero input? I would prefer at least one input. + +??: A graph may encode a constant (so, no input). Alone, such a graph will certainly nover be used in any actual use case, but a graph may also be called by a SCAN op (for instance). In that case, it may make sense to define a constant graph... + + +> Add (C8): At least one output of each node is connected either to an output +> of the graph or to an input of another node. + +> Rationale: Each component should have a function. May be redondant with [R1]. + +KO: It is actually redundant with R1 which is not a constraint in the sense that it is part of the semantics. +It is really a SONNX restriction since a valid ONNX graph may have unconnected nodes. + +> "Please note that the sub operator has been removed since it is not used + +> by any other node or as an output of the graph." + +> is contradictory with "%op4_out = Sub(%I1, %I2)". + +OK: Yes, you are absolutely right. I am wondering why I have written this…\ +=> Suppressed. + +> Behaviour: Add "Initially, all input values of the graph are defined." + +OK\: +=> Added. + +> and "When an output of a node is connected to the input of another node +> by an edge the definition of the value of the output of the first +> node implies the definition of the value of the input of the second node". + +OK/KO: This is already part of the edge definition: + +"- An edge represents a connection between the input and the output + of two different nodes, or a connection between the input (resp. output) of the graph and the input (resp. output) of a node. + +- +At any time, the inputs and outputs connected by an edge have the same value +" + +But there is a slight difference in the sense that I am only talking about "values" here. Implicitly, I consider that "undefined" is a value. Which it is not.\ +=> At any time, the inputs and outputs connected by an edge have the same status (defined/undefined) and values + + + + +> May be the definition of the graph should be exended to include + +> in its structure the special nodes. + +OK +=> Moved. + + + + + + + diff --git a/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md b/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md new file mode 100644 index 00000000..0a514914 --- /dev/null +++ b/safety-related-profile/documents/profile_graph/reviews/m.belcaid.md @@ -0,0 +1,41 @@ +Graph + +[T01] A graph is defined by: + + A set of inputs and outputs parameters + An input (resp. output) parameter is a free variable that can be bound to some tensor. + A set of local (or "internal") tensors. + A set of nodes + + +Remarks : + + -Add a schema of the context of hierarchy of the Graph, with the notion of Node and Operator + Model + └── Graph + └── Node[ ] + └── Operator (referenced by op_type) + + - "that can be bound" need to be replace by "must be bound to tensor" + (The node entries must be associated with a tensor, otherwise the calculation can't be performed) + - Add an Example: A Graph with 2 Nodes and the bindings Tensors inputs/ouputs. + + [input_tensor]--+ + |--[Add]--[add_output_tensor]--[Relu]--[output_tensor] + [const_tensor]--+ + + + +Remarks on Operators and Nodes [T03a] and [T03b] : + + - Precise that in Node " describes the binding of its inputs and outputs " is defined by name and not parameter contrary to the Operator; + -> "describes the binding of its inputs and outputs by named tensor" + - Precise that "A Node can only bind only one Operator." + - Add an example with the same Operator used multiple times. + + Example:An Operator [Add] used twiced in this Graph. + + Input a ----+ + |--[Add]--(Output c)---+ + Input b ----+ |--[Add]--( Output e) + Input d ----------------------------+ diff --git a/safety-related-profile/documents/profile_graph/reviews/mohammed.md b/safety-related-profile/documents/profile_graph/reviews/mohammed.md new file mode 100644 index 00000000..48b3c091 --- /dev/null +++ b/safety-related-profile/documents/profile_graph/reviews/mohammed.md @@ -0,0 +1,56 @@ +[ERIC] General remark: I have tried to simplify the expression as far as possible. + +Graph + +[T01] A graph is defined by: + + A set of inputs and outputs parameters + An input (resp. output) parameter is a free variable that can be bound to some tensor. + A set of local (or "internal") tensors. + A set of nodes + + +Remarks : + + -Add a schema of the context of hierarchy of the Graph, with the notion of Node and Operator + Model + └── Graph + └── Node[ ] + └── Operator (referenced by op_type) + +{ERIC] OK, figure to be added. + + - "that can be bound" need to be replace by "must be bound to tensor" + +[ERIC] KO: There is a subtle distinction between a definition ("the free variable **can** be bound to a tensor") and the constraint ("the free variable **must** be bound to a tensor for the graph to be executable".). I keep the definition as is and I ensure that the operational constraint is actually stated somewhere. + + (The node entries must be associated with a tensor, otherwise the calculation can't be performed) + - Add an Example: A Graph with 2 Nodes and the bindings Tensors inputs/ouputs. + + [input_tensor] --+ + |--[Add]--[add_output_tensor]--[Relu]--[output_tensor] + [const_tensor]--+ + + + +Remarks on Operators and Nodes [T03a] and [T03b] : + + - Precise that in Node " describes the binding of its inputs and outputs " is defined by name and not parameter contrary to the Operator; + -> "describes the binding of its inputs and outputs by named tensor" + + + - Precise that "A Node can only bind only one Operator." + +[ERIC] OK. + + - Add an example with the same Operator used multiple times. + + Example:An Operator [Add] used twice in this Graph. + + Input a ----+ + |--[Add]--(Output c)---+ + Input b ----+ |--[Add]--( Output e) + Input d ----------------------------+ + +[ERIC] OK, a complete example has been given. + diff --git a/safety-related-profile/documents/profile_opset/conv/conv.md b/safety-related-profile/documents/profile_opset/conv/conv.md deleted file mode 100644 index 21eba0af..00000000 --- a/safety-related-profile/documents/profile_opset/conv/conv.md +++ /dev/null @@ -1,701 +0,0 @@ -# Conventions -## Notations -- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$. If the tensor represents an image, $h(X)$ and $w(X)$ represent the _height_ and the _width_ of the image. -## Usage of fonts -- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`. -## Tags -- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number.\ -A synthesis of all restrictions is given in section "Restrictions". -## Types -- Operators are first described for values in the domain of real numbers. A specific description is given for the other types (floats, integers). - -# `conv` operator (real) - -### Restrictions -The following restrictions apply to the `conv` operator for the SONNX profile: -- The number of spatial axes of the tensors is restricted to 2 `[R1]` -- Attribute `auto_pad` is restricted to `NOTSET` `[R2]` -- Attribute `group` is restricted to 1 (standard convolution) or to the number of channels of the input tensor (depthwise convolution) `[R3]` -- All attributes shall be be given explicit values (i.e., default values for attributes are not supported) `[R4]` -- The number of elements in the `strides` list is equal to 2. `[R5]` - -### Signature -`Y = conv(X,W,[B])` -where -- `X`: input tensor -- `W`: convolution kernel -- `B`: optional bias -- `Y`: output tensor - -#### Informal specification - -Operator `conv` computes the convolution of the input tensor `X` with the kernel `W` and adds bias `B` to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_ `[R3]`. The SONNX profile limits the number of spatial axes to 2 `[R1]`. - -##### Standard convolution -A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where `group`= 1. - -The mathematical definition of the operator without padding is given hereafter. -The formal specification is given in Section Formal specification. When considering padding, the same formula applies, in which `X` represents the padded version of the actual input `X`. - -$$\begin{gathered} - Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} \\ (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) \\ + B[c] -\end{gathered}$$ - -Where -- $b$ is the batch index, $b \in [0,b(Y)-1]$, $b(Y)$ is the batch size of output `Y` -- $c$ is the data channel, $c \in [0,c(Y)-1]$, $c(Y)$ is the number of data channels of output `Y` -- $m \in [0,h(Y)-1]$ is the index of the first spatial axis of output `Y` -- $n \in [0,w(Y)-1]$ is the index of the second spatial axis of output `Y` -- $fm(W)$ is the number of feature maps of kernel `W` -- $h(W)$ is the size of the first spatial axis of kernel `W` -- $w(W)$ is the sizes of the second spatial axis of kernel `W` - -`strides` and `dilations` are attributes of the operator. They are described later in this section. - -The effect of the operator is illustrated on the following figure. In this example -- shape of `Y` is $1\times 1 \times 4 \times 4$ (batch size is 1, number of data channels is 1) -- shape of `X` is $1 \times 1 \times 8 \times 8$ (batch size is 1, number of data channels is 1) -- shape of `W` is $1 \times 1 \times 3 \times 2$ (number of data channels is 1) -- shape of `B` is $1$ -- `pads` is set to (1,2,2,2) (1 column on the left, 2 columns on the right, 2 rows on the top, 2 rows on the bottom) -- `dilations` is set to (2,2) -- `strides` is set to (2,3) - -![](./imgs/conv-std.png) - -The following figure shows the case where the number of channels of `X` is 3. In this example: -- shape of `Y` is $1 \times 1 \times 4 \times 4$ -- shape of `X` is $1 \times 3 \times 8 \times 8$ -- shape of `W` is $1 \times 3 \times 3 \times 2$ -- shape of `B` is $1$ -- `groups` is set to 1 -- the other attributes have the same values as in the previous figure. - -![](./imgs/conv-std-3-channels.png) - - -##### Depthwise convolution -A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where `group`= $c(X)$. - -The mathematical definition is given hereafter: - -$$\begin{gathered} - Y[b, c, m, n] = \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1}\\ (X[b, c, m \cdot strides[0] + j \cdot dilations[0], n \cdot strides[1] + z \cdot dilations[1]] \cdot W[c, 0, j , z] ) + B[c] -\end{gathered}$$ - -Variables are defined as for the standard convolution. -The effect of the operator is illustrated on the following figure. In this example, -- shape of `Y` is $1\times 3 \times 4 \times 4$ -- shape of `X` is $1 \times 3 \times 8 \times 8$ -- shape of `W` is $3 \times 1 \times 3 \times 2$ -- shape of `B` is $3$ -- `groups` is set to 3 -- the other attributes have the same values as in the previous figure. - -![](./imgs/conv-dep-3-channels.png) - -#### Inputs and outputs - -##### `X` - -Tensor `X` is the input tensor on which convolution with kernel `W` is computed. - -The shape of tensor `X` is $b(X) \times c(X) \times h(X) \times w(X)$. - -###### Constraints - -- (C1) Number of spatial axes of tensor `X` - - Statement: The number of spatial axes of tensor `X` is 2. `[R1]` - - Rationale: This restriction is intoduced to simplify the implementation considering the actual industrial use cases. -- (C2) Consistency between the number of channels of `X` and `W` - - Statement: $c(X)=fm(W)$ -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` - - - Statement: - * $$\left\lfloor{\frac{alpha-(dilations[0] \cdot h(W)-1)}{strides[0]}} \right\rfloor +1 = h(Y) \mbox{ with } alpha=h(X)+pads[0]+pads[2]$$ - - and - - * $$\left\lfloor{\frac{beta-(dilations[1] \cdot w(W)-1)}{strides[1]}} \right\rfloor +1 = w(Y) \mbox{ with } beta=w(X)+pads[1]+pads[3]$$ - - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis. -- (C4) Axis denotations - - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\]. - - Rationale: Denotation convention - -##### `W` - -Tensor `W` is the convolution kernel. - -The shape of tensor `W`is $(c(W) \times fm(W) \times h(W) \times w(W))$, where -- $c(W)$ is the number of output channels or number of feature maps -- $fm(W)$ is the number of input channels -- $h(W)$ and $w(W)$ are the sizes of the kernel for the two spatial axes. - -###### Constraints -- (C1) Consistency between the number of channels of `X` and `W` - - Statement: [See constraint (C2) of X](#channel_consist). -- (C2) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist). -- (C3) Consistency between `W` and `kernel_shape` - - - Statement: The size of `W` for an axis must bve equal to the value of `kernel_shape` for that axis - - Rationale: `kernel_shape` represents the shape of `W`, where `kernel_shape[0]` = $w(W)$ and `kernel_shape[1]` = $h(W)$. -- (C4) Axis denotations - - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\]. - - Rationale: Denotation convention - -##### `B` - -Tensor `B` is the bias. - -The shape of tensor `B`is $c(B)$. - -###### Constraints -- (C1) Consistency between the number of channels of `B` and `W` - - Statement: $c(B) = fm(W)$. - -#### Attributes - -##### `strides`: list of int - -Attribute `strides` determines how the kernel is applied on tensor `X` during the convolution. - -For instance, with $\mbox{\texttt{stride}}[0]=2$ and $\mbox{\texttt{stride}}[1]=3$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution. - -> The previous sentence is not clear... - -The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(2,3). - - - -###### Constraints -- (C1) Size of `strides` - - Statement: the number of elements in the `strides` list is equal to 2. `[R5]` - - Rationale: The SONNX profile only supports 2 spatial axes. -- (C2) Value domain - - Statement: `strides` is a list of strictly positive integers. - - Rationale: Stride values are used in the denominator of expression in [constraint (C3) of X](#shape_consist) -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `auto_pad` : string - -The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X. - -###### Constraints -- (C1) Explicit padding - - Statement: `auto_pad` shall be set to `NOTSET` `[R3]` - - Rationale: The SONNX profile imposes explicit padding. - -##### `pads`: list of int - -Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor `X`. - -`pads` is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$. - -The padding value is 0. - -The effect of the `pads` attribute is illustrated on the following figure. In this example, `pads`=(1,3,2,2). - - - -###### Constraints -- (C1) Value domain - - Statement: `pads` is a list of positive or null integers. - - Rationale: A padding value gives a number of elements to be added to some spatial axis. This is positive[^2]. -- (C2) Consistency between the shape of `X` and the length of `pads` - - Statement: The length of the `pads` list is two times the number of spatial axes of `X` - - Rationale: Padding shall be given for all spatial axes, and a beggining value and an end value must be given for each axis. -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `dilations`: list of int - -Attribute `dilations` specifies the spacing between the kernel elements for each spatial axis of the filter `W`. It is a list of non-null integer values where each value gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel points are spaced out by the dilation factor for that axis. - -The spacing value is 0. - -The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. In this example, `dilations`=(2,2). - - - - -###### Constraints -- (C1) Value domain - - Statement: `dilations` is a list of strictly positive integers -- (C2) Relation between `dilations` and `W` - - Statement: The length of the `dilations` list is equal to number of spatial axes of `W`. - - Rationale: Dilation is defined for all spatial axes of `W`. -- (C3) Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides`. - - Statement: [See constraint (C3) of X](#shape_consist) - -##### `group`: int - -This attribute specifies the number of groups the input channels and output channels are divided into. When `group`=1, a standard convolution is performed. -When group is greater than 1, convolution is computed for each group separately with a specific set of filters. - -The effect of the `group` attribute for a tensor with two spatial axes is depicted on the following figure. In this example `group`=3. - - - -(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning) - -In the example, with `group` set to 3 and an input `X` and an output `Y` with 3 channels, the input and output channels will be divided into 3 groups of 1 channel. - -###### Constraints -- (C1) Support for standard and depthwise convolutions - - Statement: `group`=1 (standard convolution) or `group`$=c(X)$ (depthwise convolution) - - Rationale: SONNX only supports standard and depthwise convolutions - -##### `kernel_shape`: list of int - -This parameter specifies the shape of the convolution kernel `W`. - -###### Constraints. - -- (C1) Value domain - - Statement: `kernel_shape` is a list of strictly positive integers - - Rationale: A dimension is always positive and cannot be null. -- (C2) Consistency between `W` and `kernel_shape` - - Statement: [See constraint (C3) of W](#kernel_shape_w) - -#### Outputs - -##### `Y` - -The size of the output `Y` will be $(b(Y) \times c(Y) \times h(Y) \times w(Y))$ where -- $b(Y)$ is the number of batches -- $c(Y)$ is the number of channels -- $h(Y)$ and $w(Y)$ are the sizes of the output for the two spatial axes - -###### Constraints. -- (C1) Consistency between the number of channels of `B` and `Y` - - Statement: HYPERLIEN VERS W / constraint kernel shape -- (C2) Consistency between the shape of tensors `X`, `W`, `Y`, attributes `pads` and `strides`, - - Statement: [see constraint (C3) of X](#shape_consist) - -#### Formal specification - -The following code specifies the `conv` operator using the Why3 language[^3]. - -###### Nota: the specification does not cover all attributes values. Currently, there is no padding (`pads` is not set and `auto_pad = NOTSET`) and `dilations` is not set. - -``` ocaml -module Conv - use int.Int - use real.RealInfix - use array.Array - use int.ComputerDivision - use real.Truncate - use real.FromInt - - type input_tensor = { - x: array real; - x_l: int; - x_c: int; - x_b: int; - x_ch: int; - } - - type convolution_kernel = { - w: array real; - w_l: int; - w_c: int; - w_ch_in: int; - w_ch_out: int; - } - - type bias_tensor = { - b: array real; - b_c: int; - } - - type attributes = { - stride: array int; - pads: array int; - auto_pad: int; - dilation: array int; - } - - type output_tensor = { - - y_b: int; - y_ch: int; - y_l: int; - y_c: int; - - } - - function conv_size (out: output_tensor) : int = - out.y_b * out.y_ch * out.y_l * out.y_c - - predicate conv_result - (inp: input_tensor) - (kernel: convolution_kernel) - (bias: bias_tensor) - (attr: attributes) - (out: output_tensor) - (res: array real) - (bi ci hi wi: int) - (ci_in ki_h ki_w: int) = - let y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi in - let x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] in - let x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] in - - (0 <= x_l_idx < inp.x_l /\ 0 <= x_c_idx < inp.x_c) -> - let x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx in - let w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w in - res.elts (y_idx) = bias.b[ci] +. (inp.x[x_idx] *. kernel.w[w_idx]) - - val conv (inp: input_tensor)(kernel: convolution_kernel)(bias: bias_tensor)(attr: attributes)(out: output_tensor): array real - requires{inp.x_ch = out.y_ch = kernel.w_ch_in = bias.b_c} - requires{out.y_l = (div (inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l)) attr.stride[0]) + 1} - requires{out.y_c = (div (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c)) attr.stride[1]) +1} - requires { inp.x_l > 0 /\ inp.x_c > 0 /\ inp.x_ch > 0 /\ inp.x_b > 0} - requires{kernel.w_l > 0 /\ kernel.w_c > 0 /\ kernel.w_ch_in > 0 /\ kernel.w_ch_out > 0} - requires { out.y_b > 0 /\ out.y_ch > 0 /\ out.y_l > 0 /\ out.y_c > 0} - requires { length inp.x = inp.x_l * inp.x_c * inp.x_ch * inp.x_b} - requires{length kernel.w = kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out} - requires{inp.x_l >= kernel.w_l} - requires{inp.x_c >= kernel.w_c} - requires{length bias.b = bias.b_c} - requires{length attr.stride = 2} - requires{length attr.dilation = 2} - requires{length attr.pads = 4} - requires{forall i. 0 <= i < length attr.pads -> attr.pads[i] = 0} - requires{forall j. 0 <= j < length attr.dilation -> attr.dilation[j] >= 1} - requires{forall k. 0 <= k < length attr.stride -> attr.stride[k] >= 1} - ensures { length result = conv_size out } - ensures { forall bi ci hi wi ci_in ki_h ki_w: int. - 0 <= bi < out.y_b -> - 0 <= ci < out.y_ch -> - 0 <= hi < out.y_l -> - 0 <= wi < out.y_c -> - 0 <= ci_in < kernel.w_ch_in -> - 0 <= ki_h < kernel.w_l -> - 0 <= ki_w < kernel.w_c -> conv_result inp kernel bias attr out result bi ci hi wi ci_in ki_h ki_w } - -end - - -module Test_conv - use int.Int - use real.RealInfix - use array.Array - use int.ComputerDivision - use real.Truncate - use real.FromInt - use Conv - -let test_conv () = - let inp_x = Array.make 9 1.0 in - let inp = { x = inp_x; x_l = 3; x_c = 3; x_b = 1; x_ch = 1 } in - - let kernel_w = Array.make 4 0.0 in - let kernel = { w = kernel_w; w_l = 2; w_c = 2; w_ch_in = 1; w_ch_out = 1 } in - - let bias_b = Array.make 1 0.5 in - let bias = { b = bias_b; b_c = 1 } in - let stride = Array.make 2 1 in (* Stride of 1 *) - let pads = Array.make 4 0 in (* No padding *) - let dilation = Array.make 2 1 in (* Dilation of 1 *) - let attr = { stride = stride; pads = pads; auto_pad = 0; dilation = dilation } in - let out_h = (div (inp.x_l + pads[0] + pads[2] - (dilation[0] * kernel.w_l)) stride[0]) + 1 in - let out_w = (div (inp.x_c + pads[1] + pads[3] - (dilation[1] * kernel.w_c)) stride[1]) + 1 in - let out = { y_b = 1; y_ch = 1; y_l = out_h ; y_c = out_w } in - (* Call the conv function *) - let result = conv inp kernel bias attr out in - let actual_result = result.elts 0 in - assert { conv_result inp kernel bias attr out result 0 0 0 0 0 0 0} ; - assert { actual_result = 0.5 } ; - () - -end -``` - -Another formal specification of the `conv` operator using Frama-C -language[^4] is presented below. - -``` objectivec -#include -#include -#include - -/* Data Structures */ -typedef struct { - float *x; - int x_l, x_c, x_b, x_ch; -} input_tensor; - -typedef struct { - float *w; - int w_l, w_c, w_ch_in, w_ch_out; -} convolution_kernel; - -typedef struct { - float *b; - int b_c; -} bias_tensor; - -typedef struct { - int *stride; - int *pads; - int *dilation; -} attributes; - -typedef struct { - float *y; - int y_b, y_ch, y_l, y_c; -} output_tensor; -/*@ - requires \valid_read(pads + (0 .. 3)); - requires \valid_read(stride + (0 .. 1)); - requires \valid_read(result + (0 .. 3)); - requires x_l > 0 && x_c > 0 && w_l > 0 && w_c > 0 && y_l > 0 && y_c > 0; - assigns result[0 .. 3]; - behavior empty_or_notset: - assumes (auto_pad == "") || (auto_pad == "NOTSET"); - ensures \forall integer i; 0 <= i < 4 ==> result[i] == pads[i]; - -behavior valid: - assumes (auto_pad == "VALID") ; - ensures \forall integer i; 0 <= i < 4 ==> result[i] == 0; - -behavior same_upper: - assumes (auto_pad == "SAME_UPPER") ; - ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l; - \let pad_c = (y_c - 1) * stride[1] + w_c - x_c; - ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==> - (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)) && - (pad_l % 2 != 0) && (pad_c % 2 != 0) ==> - (result[0] == (pad_c / 2) && result[1] == (pad_l / 2) && result[2] == ((pad_c / 2) + 1) && result[3] == ((pad_l / 2) + 1)); - -behavior same_lower: - assumes (auto_pad == "SAME_LOWER"); - ensures \let pad_l = (y_l - 1) * stride[0] + w_l - x_l; - \let pad_c = (y_c - 1) * stride[1] + w_c - x_c; - ((pad_l % 2 == 0) && (pad_c % 2 == 0)) ==> - (result[0] == (pad_c / 2) && result[2] == (pad_c / 2) && result[1] == (pad_l / 2) && result[3] == (pad_l == 2)) && - ((pad_l % 2 != 0) && (pad_c % 2 != 0)) ==> - (result[0] == ((pad_c / 2) + 1) && result[1] == ((pad_l / 2) + 1) && result[2] == (pad_c / 2) && result[3] == (pad_l / 2)); -complete behaviors empty_or_notset, valid, same_upper, same_lower; -disjoint behaviors empty_or_notset, valid, same_upper, same_lower; -*/ -void compute_pad(const char* auto_pad, int pads[4], int stride[2], int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int result[4]) { - int pad_l, pad_c; - - if ((auto_pad == "") || (auto_pad == "NOTSET")) { - for (int i = 0; i < 4; i++) { - result[i] = pads[i]; - } - } else if ((auto_pad == "VALID")) { - for (int i = 0; i < 4; i++) { - result[i] = 0; - } - } else if ((auto_pad == "SAME_UPPER")) { - pad_l = (y_l - 1) * stride[0] + w_l - x_l; - pad_c = (y_c - 1) * stride[1] + w_c - x_c; - - if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) { - result[0] = result[2] = pad_c / 2; - result[1] = result[3] = pad_l / 2; - } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) { - result[0] = pad_c / 2; - result[1] = pad_l / 2; - result[2] = (pad_c / 2) + 1; - result[3] = (pad_l / 2) + 1; - } - } else if ((auto_pad == "SAME_LOWER")) { - pad_l = (y_l - 1) * stride[0] + w_l - x_l; - pad_c = (y_c - 1) * stride[1] + w_c - x_c; - - if ((pad_l % 2 == 0) && (pad_c % 2 == 0)) { - result[0] = result[2] = pad_c / 2; - result[1] = result[3] = pad_l / 2; - } else if ((pad_l % 2 != 0) && (pad_c % 2 != 0)) { - result[0] = (pad_c / 2) + 1; - result[1] = (pad_l / 2) + 1; - result[2] = pad_c / 2; - result[3] = pad_l / 2; - } - } -} - -/* Function to compute the convolution */ -//void compute_pad(int auto_pad, int *pads, int *stride, int x_l, int x_c, int w_l, int w_c, int y_l, int y_c, int *result); - -/*@ - requires \valid_read(inp.x + (0..(inp.x_l*inp.x_c*inp.x_b*inp.x_ch)-1)); - requires \valid_read(kernel.w + (0..(kernel.w_l*kernel.w_c*kernel.w_ch_in*kernel.w_ch_out)-1)); - requires \valid_read(bias.b + (0..bias.b_c-1)); - requires \valid_read(attr.stride+(0..1)); - requires \valid_read(attr.pads+(0..3)); - requires \valid_read(attr.dilation+(0..1)); - requires inp.x_ch == out.y_ch; - requires out.y_ch == kernel.w_ch_in; - requires kernel.w_ch_in == bias.b_c; - requires out.y_l == ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1; - requires out.y_c == ((inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) + 1; - requires inp.x_l > 0 && inp.x_c > 0 && inp.x_ch > 0 && inp.x_b > 0; - requires kernel.w_l > 0 && kernel.w_c > 0 && kernel.w_ch_in > 0 && kernel.w_ch_out > 0; - requires bias.b_c > 0; - requires out.y_l > 0 && out.y_c > 0 && out.y_ch > 0 && out.y_b > 0; - requires inp.x_l >= kernel.w_l; - requires inp.x_c >= kernel.w_c; - requires \forall integer i; 0 <= i < 4 ==> attr.pads[i] >= 0; - requires \forall integer i; 0 <= i < 2 ==> attr.dilation[i] >= 1; - requires \forall integer i; 0 <= i < 2 ==> attr.stride[i] >= 1; - - - assigns out.y[0..(out.y_b * out.y_ch * out.y_l * out.y_c)-1]; - - ensures \forall integer bi, ci, hi, wi, ci_in, ki_h, ki_w; - 0 <= bi < out.y_b ==> - 0 <= ci < out.y_ch ==> - 0 <= hi < out.y_l ==> - 0 <= wi < out.y_c ==> - 0 <= ci_in - 0 <= ki_h < kernel.w_l ==> - 0 <= ki_w < kernel.w_c ==> - (0 <= hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) && - (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0] < inp.x_l) && - (0 <= wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]) && - (wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1] < inp.x_c) ==> - out.y[bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi] == inp.x[bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + (hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]) * inp.x_c + ( wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1])] * kernel.w[ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w] + bias.b[ci]; - - - - - - - - */ -float* conv(input_tensor inp, convolution_kernel kernel, bias_tensor bias, attributes attr, output_tensor out) { - - out.y_l = ((inp.x_l + attr.pads[0] + attr.pads[2] - (attr.dilation[0] * kernel.w_l )) / attr.stride[0]) + 1; - out.y_c = ( (inp.x_c + attr.pads[1] + attr.pads[3] - (attr.dilation[1] * kernel.w_c )) / attr.stride[1]) +1; - int y_size = out.y_b * out.y_ch * out.y_l * out.y_c; - out.y = (float *)malloc(y_size * sizeof(float)); - - if (out.y == NULL) { - fprintf(stderr, "Memory allocation failed\n"); - exit(EXIT_FAILURE); - } - - // Compute padding - // compute_pad(attr.auto_pad, attr.pads, attr.stride, inp.x_l, inp.x_c, kernel.w_l, kernel.w_c, out.y_l, out.y_c, attr.pads); - - // Initialize result tensor to bias values - for (int bi = 0; bi < out.y_b; ++bi) { - for (int ci = 0; ci < out.y_ch; ++ci) { - for (int hi = 0; hi < out.y_l; ++hi) { - for (int wi = 0; wi < out.y_c; ++wi) { - int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi; - out.y[y_idx] = bias.b[ci]; - } - } - } - } - - // Convolution computation - for (int bi = 0; bi < out.y_b; ++bi) { - for (int ci = 0; ci < out.y_ch; ++ci) { - for (int hi = 0; hi < out.y_l; ++hi) { - for (int wi = 0; wi < out.y_c; ++wi) { - int y_idx = bi * (out.y_ch * out.y_l * out.y_c) + ci * (out.y_l * out.y_c) + hi * out.y_c + wi; - - for (int ci_in = 0; ci_in < kernel.w_ch_in; ++ci_in) { - for (int ki_h = 0; ki_h < kernel.w_l; ++ki_h) { - for (int ki_w = 0; ki_w < kernel.w_c; ++ki_w) { - int x_l_idx = hi * attr.stride[0] + ki_h * attr.dilation[0] - attr.pads[0]; - int x_c_idx = wi * attr.stride[1] + ki_w * attr.dilation[1] - attr.pads[1]; - - if (x_l_idx >= 0 && x_l_idx < inp.x_l && x_c_idx >= 0 && x_c_idx < inp.x_c) { - int x_idx = bi * (inp.x_ch * inp.x_l * inp.x_c) + ci_in * (inp.x_l * inp.x_c) + x_l_idx * inp.x_c + x_c_idx; - int w_idx = ci * (kernel.w_ch_in * kernel.w_l * kernel.w_c) + ci_in * (kernel.w_l * kernel.w_c) + ki_h * kernel.w_c + ki_w; - - if (x_idx < inp.x_l * inp.x_c * inp.x_ch * inp.x_b && w_idx < kernel.w_l * kernel.w_c * kernel.w_ch_in * kernel.w_ch_out) { - out.y[y_idx] += inp.x[x_idx] * kernel.w[w_idx]; - } - } - } - } - } - } - } - } - } - - return out.y; -} -``` - -# Graph execution semantics - -
- -Elements of the execution semantics is given on the [IR (Intermediate -Representation) page](https://onnx.ai/onnx/repo-docs/IR.html) of the -ONNX web site. In addition, a Python “reference implementation” is also -provided (see ). The source -code of this implementation can be found at -. - -Very informally, the semantics is pretty simple: each operator (or -function) is called according to its position in the topological sorting -of the operators. The topological order is a partial order that ensures -that an operator is executed only when its inputs are available. Being a -partial order, it means that several valid orders exist for a given -graph. Normally (?) each order should generate the same result, even in -the presence of floating point operations. - -The Python code to execute a graph is given in class -[`ReferenceEvaluator`](https://github.com/onnx/onnx/blob/main/onnx/reference/reference_evaluator.py)). -After having processed the inputs and the initializers (i.e., fed the -`results` dictorionary with these data), the nodes are executed in -sequence. For each operator, the interpretor checks that its inputs are -in the `results` dictionary. If they are not, an error is raised (if the -operators are listed in topological order, this situation should not -occur). Otherwise, the operator is simply executed (method `run`) with -or without a context (composed of the current results) depending on the -type of operators. (Check that this does not create a dependency to the -total order of operators.) - -
- -### Informal specification - -
- -The semantics of an ONNX model is given in Section "Model Semantics" of -the [Intermediate -Representation](https://github.com/onnx/onnx/blob/main/docs/IR.md) page. -Basically, an inference-model is a stateless function (except possibly -for some specific nodes such as a random-generation node) represented by -an acyclic `graph` of nodes. The `graph` is mainly represented by a set -of inputs and outputs and a topologically sorted list of nodes. Each -node represents a call to an operator or a function. A `function` is -itself a graph. - -Note that the types of inputs and outputs are not systematically -required because they can be inferred. In our case, I guess that we will -forbib shape inference and rely on static tensor shapes (or, at least, -shape inference can be bone before serializing the model). The proecss -of shape inference is described in Section  [ONNX Shape -Inference](https://onnx.ai/onnx/repo-docs/ShapeInference.html). - -
- -### Formal specification - -*To be completed.* - -[^1]: At least in a first phase... - -[^2]: Note: in the ONNX runtime implementation, the padding value may be - negative, which corresponds to reducing the size of the tensor. - -[^3]: See [Why3 documentation](https://www(W)hy3.org/) - -[^4]: See [Frama-C - documentation](https://www.frama-c.com/html/documentation.html) diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/README.md b/safety-related-profile/documents/profile_opset/conv/imgs/README.md deleted file mode 100644 index 4a389a0a..00000000 --- a/safety-related-profile/documents/profile_opset/conv/imgs/README.md +++ /dev/null @@ -1 +0,0 @@ -Images used in the CONV specification example diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png b/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png deleted file mode 100644 index 614f6942..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/autopad.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png deleted file mode 100644 index 54b8ab1c..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep-3-channels.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png deleted file mode 100644 index 54b8ab1c..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-dep.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png deleted file mode 100644 index 8b1b8d10..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3 channels.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png deleted file mode 100644 index 8b1b8d10..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std-3-channels.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png deleted file mode 100644 index 660284cd..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv-std.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv.png deleted file mode 100644 index 660284cd..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png deleted file mode 100644 index 2679ecbc..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv_pad.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png b/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png deleted file mode 100644 index eed05a17..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/conv_stride.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png b/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png deleted file mode 100644 index dfdf868c..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/grouped_convolution.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio b/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio deleted file mode 100644 index 8585004a..00000000 --- a/safety-related-profile/documents/profile_opset/conv/imgs/onnx_conv.drawio +++ /dev/null @@ -1,3433 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png b/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png deleted file mode 100644 index 97ceb89e..00000000 Binary files a/safety-related-profile/documents/profile_opset/conv/imgs/why3_workflow.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png b/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png deleted file mode 100644 index 24058c54..00000000 Binary files a/safety-related-profile/documents/profile_opset/where/imgs/ONNX_WHERE.png and /dev/null differ diff --git a/safety-related-profile/documents/profile_opset/where/imgs/README.md b/safety-related-profile/documents/profile_opset/where/imgs/README.md deleted file mode 100644 index 325698b2..00000000 --- a/safety-related-profile/documents/profile_opset/where/imgs/README.md +++ /dev/null @@ -1 +0,0 @@ -Images for the WHERE operator diff --git a/safety-related-profile/documents/profile_opset/where/where.md b/safety-related-profile/documents/profile_opset/where/where.md deleted file mode 100644 index 5185a0a7..00000000 --- a/safety-related-profile/documents/profile_opset/where/where.md +++ /dev/null @@ -1,259 +0,0 @@ -# Conventions -## Notations -- The notation $N(X)$ denotes the _number of elements_ in tensor $X$. -- Notations $h(X)$ and $w(X)$ respectively denote the _height_ and the _width_ of tensor $X$. - - -## Usage of fonts -- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "condition" attribute is represented by `condition`. - -## Tags -- Restrictions with respect to the ONNX standard are indicated in the text with the tag `[Ri]` where `i` is a number. A synthesis of all restrictions is given in section "Restrictions". - -## Types -- Operators are first described for values in the domain of real numbers. Because the `WHERE` put in ouput the same type of X or Y according to the condition the ouput is of the same type of X or Y and could be of every possible type as tensor(bfloat16), tensor(bool), tensor(complex128), tensor(complex64), tensor(double), tensor(float), tensor(float16), tensor(int16), tensor(int32), tensor(int64), tensor(int8), tensor(string), tensor(uint16), tensor(uint32), tensor(uint64), tensor(uint8). - -# `where` operator (real) - -### Restrictions -The following restrictions apply to the `where` operator for the SONNX profile: -- The tensors `condition`, `X`, and `Y` must have the same shape `[R1]` -- The operator does not support sparse tensors `[R2]` -- All X and Y inputs elements shall have the same explicit types `[R3]` -- No broadcasting allowed for the tensors `condition`, `X`, and `Y` even if they are broadcastable to a common shape, the broadcasting is forbidden because dynamic computation time according to the shape is not deterministe (depend of dynamic dimension X,Y,Z and could crash) `[R4]` - -### Signature -`Z = where(condition, X, Y)` -where -- `condition`: a tensor of boolean values where 0 represents False and non-zero represents True -- `X`: input tensor to pick values from when `condition` is True -- `Y`: input tensor to pick values from when `condition` is False -- `Z`: output tensor based on the `condition`, `X`, and `Y` - -#### Informal specification - -The `where` operator selects elements from two input tensors `X` and `Y` based on the values in the `condition` tensor. For each element, if the corresponding entry in `condition` is True, the resulting tensor `Z` will contain the element from `X`. Otherwise, the resulting tensor `Z` will contain the element from `Y`. - -The mathematical definition of the operator is given hereafter. - -$$ -Z[i] = -\begin{cases} -X[i] & \text{if } condition[i] & \text{is different of false} \\ -Y[i] & \text{otherwise} -\end{cases} -$$ - -Where -- $i$ is an index covering all dimensions of the tensors. - -The effect of the operator is illustrated on the following examples. In these examples: -- `condition` is a tensor of boolean values -- `X` and `Y` are tensors holding numerical data - - - -Example 1: -```math -`condition` = \begin{bmatrix} True & False & True \end{bmatrix} -``` -```math -`X` = \begin{bmatrix} 9 & 8 & 7 \end{bmatrix} -``` -```math -`Y` = \begin{bmatrix} 6 & 5 & 4 \end{bmatrix} -``` -Result `Z` will be : -```math -`Z` = \begin{bmatrix} 9 & 5 & 7 \end{bmatrix} -``` - - -Example 2: -```math -`condition` = \begin{bmatrix} True & True \\ True & False \\ False & True \end{bmatrix} -``` -```math -`X` = \begin{bmatrix} 1 & 2 \\ 3 & 4 \\ 5 & 6 \end{bmatrix} -``` -```math -`Y` = \begin{bmatrix} 12 & 11 \\ 10 & 9 \\ 8 & 7 \end{bmatrix} -``` -Result `Z` will be : -```math -`Z` = \begin{bmatrix} 1 & 2 \\ 3 & 9 \\ 8 & 6 \end{bmatrix} -``` - -Note in python is is equivalent to do : -```python ->>> import numpy as np -np.where([[True, True], [True, False],[False, True]],[[1, 2], [3, 4],[5, 6]],[[12, 11], [10, 9], [8,7]]) -array([[1, 2], - [3, 9], - [8, 6]]) -``` - -#### Inputs and outputs - -##### `condition` - -Tensor `condition` is a tensor of boolean values indicating which elements to choose from `X` and `Y`. - -The shape of tensor `condition` should be the same as `X` and same as `Y`. `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -###### Constraints - -- (C1) Consistency in shape - - Statement: The shapes of `condition`, `X`, and `Y` must be the same. $N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -##### `X` - -Tensor `X` is one of the two input tensors from which elements are picked based on the `condition`. - -The shape of tensor `X` should be the same as `condition` and `Y`. $N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -###### Constraints - -- (C1) Consistency in shape - - Statement: The shapes of `condition`, `X`, and `Y` must be the same. `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -##### `Y` - -Tensor `Y` is one of the two input tensors from which elements are picked based on the `condition`. - -The shape of tensor `Y` should be the same as `condition` and `X`. `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -###### Constraints - -- (C1) Consistency in shape - - Statement: The shapes of `condition`, `X`, and `Y` must be the same. $N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -#### Outputs - -##### `Z` - -The tensor `Z` is the output tensor formed by picking values from `X` and `Y` based on `condition`. - -`Z` will have the resulting shape of `condition`, `X`, and `Y` and must be the same. `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - -###### Constraints - -- (C1) Consistency in shape - - Statement: The shape of `Z` will match the `condition`, `X`, and `Y` shape. $N(Z)=N(condition)=N(X)=N(Y)$ `[R1]`. Broadcastable tensor is forbidden.`[R4]`. - - -#### Attributes -The `where` operator does not require any attributes. - -### Formal specification - -The formal specification of the `where` operator using the Why3 language[^1] is provided below. This specification ensures the consistency and desired behavior of the operator within the constraints described. - -```ocaml -module Where - use int.Int - use bool.Bool - use array.Array - - type tensor = { - data: array real; - dims: array int; (* tensor dimension *) - } - - function size (t: tensor) : int = - let rec product (a: array int) (i: int) : int = - if i = length a then 1 else a[i] * product a (i + 1) - in product t.dims 0 - - predicate same_dimensions (dims1: array int) (dims2: array int) : bool = - length dims1 = length dims2 /\ - (forall i: int. 0 <= i < length dims1 -> dims1[i] = dims2[i]) - - predicate where_result - (cond: tensor) - (X: tensor) - (Y: tensor) - (Z: tensor) - (i: int) = - (cond.data[i] <> 0 -> Z.data[i] = X.data[i]) /\ - (cond.data[i] = 0 -> Z.data[i] = Y.data[i]) - - val where (cond: tensor) (X: tensor) (Y: tensor): tensor - requires { same_dimensions cond.dims X.dims } - requires { same_dimensions cond.dims Y.dims } - requires { same_dimensions X.dims Y.dims } - ensures { Z.dims = cond.dims } - ensures { length Z.data = size Z } - ensures { forall i: int. 0 <= i < length Z.data -> where_result cond X Y Z i } -``` - -### Specification en Frama-C -Another formal specification of the conv operator using Frama-C language is presented below[^2]. - - -```c -#include - -/* Tensor type definition */ -typedef struct { - double* data; - int* dims; - int num_elements; // Total Number of element in data - int num_dims; // Number of dimensions -} tensor; - -/* Function in order to compute the size total Number of element in a tensor */ -int size(const tensor* t) { - int product = 1; - for (int i = 0; i < t->num_dims; i++) { - product *= t->dims[i]; - } - return product; -} - -/* Function to check if two sets of dimensions are the sames */ -bool same_dimensions(const int* dims1, int len1, const int* dims2, int len2) { - if (len1 != len2) return false; - for (int i = 0; i < len1; ++i) { - if (dims1[i] != dims2[i]) { - return false; - } - } - return true; -} - -/*@ predicate where_result { - ensures (cond->data[i] != 0 ==> Z->data[i] == X->data[i]) && - (cond->data[i] == 0 ==> Z->data[i] == Y->data[i]); - } -*/ - -/*@ requires same_dimensions(cond->dims, cond->num_dims, X->dims, X->num_dims); - requires same_dimensions(cond->dims, cond->num_dims, Y->dims, Y->num_dims); - requires same_dimensions(X->dims, X->num_dims, Y->dims, Y->num_dims); - - ensures Z->num_dims == cond->num_dims; - ensures Z->num_elements == size(Z); - ensures \forall int i; 0 <= i < Z->num_elements ==> where_result(cond, X, Y, Z, i); - - @*/ -tensor where(const tensor* cond, const tensor* X, const tensor* Y) { - tensor Z; - - for (int i = 0; i < Z.num_elements; ++i) { - if (cond->data[i] != 0) { - Z.data[i] = X->data[i]; - } else { - Z.data[i] = Y->data[i]; - } - } - - return Z; -} -``` - - -[^1]: See [Why3 documentation](https://www(W)hy3.org/) - -[^2]: See [Frama-C - documentation](https://www.frama-c.com/html/documentation.html) diff --git a/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf b/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf new file mode 100644 index 00000000..297b5a74 Binary files /dev/null and b/safety-related-profile/documents/publications/ERTS2026/2025-06-02 SONNX - ERTS 2026 abstract - final.pdf differ diff --git a/safety-related-profile/documents/reqs.md b/safety-related-profile/documents/reqs.md deleted file mode 100644 index 8612bea7..00000000 --- a/safety-related-profile/documents/reqs.md +++ /dev/null @@ -1 +0,0 @@ -File moved [here](../deliverables/reqs/reqs.md). \ No newline at end of file diff --git a/safety-related-profile/meetings/Local WGs/Tlse/agenda.md b/safety-related-profile/meetings/Local WGs/Tlse/agenda.md new file mode 100644 index 00000000..52062023 --- /dev/null +++ b/safety-related-profile/meetings/Local WGs/Tlse/agenda.md @@ -0,0 +1,412 @@ +#### 2025/06/05 +- Discussion about the relation between the informal and formal spec. Are we really convinced on the benefits of formal spec if we don't add functional invariants? See e.g. ![](../Tlse/attachments/conv_informa_formal.png). +- Check [this ONNX PR](https://github.com/onnx/onnx/issues/8054#issuecomment-4622938091) + +##### Minutes + +### Meeting Notes +- Discussion about formal specification + - The status + - Before being an input to the code generation process, the formal specification is **a specification**. So it shall define precisely what an operator shall do. + - Initially, we wanted to have a formal spec that is "close to the informal spec". We used to sad that the formal spec (i) should allow the user to express as intuitively as possible what he/she has in mind, (ii) should be easily traceable to the informal spec. + - As of today, this is not really the case. + - First, the formal spec is very large since it covers two levels of spec (abstract and concrete) and contains code only needed for the proof. + - In the abstract spec, specification often use induction, which slightly differs to the using of loops (sums, products) in the informal spec. + - The notation is a bit complicated + - For the **Conv** operator, for instance, the informal spec is specified using simple nested loops whereas a the abstract formal spec is broken down in a series of functions, each corresponding to a level of loops, and expressed inductively. + - The concrete specification is actually closer to the informal spec. + - So the first question is: **What can we do to facilitate the validation of the formal spec ?** + - Stick as much as possible to the notations used in the informal spec + - Introduce functions that would "mimick" the classical $\sum$ , or $\prod$ ? + - Improve the formal specification guidelines with rules to improve traceability? + - Finally, the main usage of the formal spec is to support the automatic generation of the implementation. But for most operators, the implementation is not complex once the specification is available, so being able to generate it automatically is interesting but not fundamental. So, besides being used to generate the code another use of the formal spec could be to verify some interesting properties of the spec, and so improve our confidence in the spec + - For **Conv**, we have for instance the following properties: + - **Conv**(X, W1 + W2) = **Conv**(X, W1) + **Conv**(X, W2) + - **Conv**(a X, W) = a **Conv**(X, W) + - **Conv**(X, a W) = a **Conv**(X, W) + - **Conv**(0, W) = 0 + - **Conv**(X, 0) = 0 + - **Conv**(X, W, B) = Conv(**X**, W, 0) + broadcast(B) + - permutation symmetry + - identity when applying a 1x1 kernel + - For stride 1, dilation 1, and no padding, shifting the input down and right by one cell should shift the output down and right by one cell, on the overlapping output domain. + - etc. + - If we could verify those properties on the formal spec, this would contribute to our confidence on the spec. + - The question is: **are such properties provable using Why3 with a reasonable effort?** +- Templating... + - We see that whatever our effort, + - we leave some errors in the informal spec, + - we have hard times keeping the existing specs in-line with the ever-changing guidelines. + - One solution could be to create a template that would capture the recurrent part of the spec. + - [ ] Propose a templating scheme [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 hivaem + - An additional solution consists to apply a LLM to check our production. First tests show the efficiency of the approach. +- [X] In **MatMul**, exchange the **Float** and **Int** sections [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 8b7vr2 +- We are still experiencing formatting issues with formulas. + - [ ] Check and correct formatting problems [project::[[SONXX]]] [assignee::[[SONNX]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 azjdc5 +- For the **Maxpool** operator. + - We keep the use of the padded kernel $X_p$ because it is intuitive (even if we know that a real implementation will never create this padded kernel...). + - The existing formula for the indice have to be updated. They will be using the $argmax$ + - [ ] Update the **Maxpool** operator to reflect the decision taken about its specification [project::[[SONXX]]] [assignee::[[SOUYRIS Jean]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 tspe86 + - [X] Send a mail to J&R to explain how the indice is computed [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 l2vlgb + - Mail sent on 08/06/2026 +- Hyperlinks + - Update hyperlinks according to what has been done for Div. +- Use of mathematical operators + - Most operators refer to mathematical operators such as $+$ or $\times$ (including in $\sum$ or $\prod$ ), but these operators are not defined. For floating point number, it seems that we have made a general statement saying that they refer to IEEE, but not for int. + - One possibility could be to specify operators using the **Add** and **Mul** operators (e.g, for scalar). But this will make the informal spec harder to read. + - The other solution can be to use the usual $+$ and $\times$ and add a general notice saying that whenever those operators are used, they respectively refer to **Add** and **Mul**. + - We opt for the second solution. + - [X] Add a general notice saying that whenever those operators are used, they respectively refer to **Add** and **Mul**. [project::[[SONXX]]] [assignee::[[JENN Eric]]] 📅 2026-06-15 ➕ 2026-06-05 🆔 afe7zu + - This notice shall belong to the "SONNX Reader user manual" + - First version of the ["User manual"](../../../sonnx/ops/docs/guidelines/sonnx_user_manual.md) created. + + + +#### 2025/05/21 +- Jean-Loup comments on [testing strategy](../../../sonnx/ops/docs/guidelines/tests-part2.md) +- Discussion about the use of Hypothesis +- Review of Franck guidelines for accuracy evaluation +- Review of pending informal spec. + +###### New actions +- [ ] Update the specification guidelines to match with the current practice (see **Div**) [project::[[SONNX]]]➕ 2026-05-21 +- [X] Update the tagging of requirements of all specs (see **Div**) [project::[[SONNX]]]➕ 2026-05-21 +- [ ] Add tags on the general constraints [assignee::[[SOUYRIS Jean]]] ➕ 2026-05-21 +- [ ] Rename section "informal specification" to "Function" [project::[[SONNX]]] ➕ 2026-05-21 +- [ ] In the signature section, add the types (see **Div**). [project::[[SONNX]]] ➕ 2026-05-21 +- [ ] Have a closer look at the actual benefits of Hypothesis to support our test strategy [project::[[SONNX]]] ➕ 2026-05-21 +- [ ] Update the priority list of operators [project::[[SONNX]]] [assignee::[[SOUYRIS Jean]]]] ➕ 2026-05-21 +- [X] Update the **Add** operator to match the latest update to **Div** [project::[[SONNX]]] [assignee::[[SOUYRIS Jean]]] ➕ 2026-05-21 +- [ ] Update the test strategy [project::[[SONNX]]] ➕ 2026-05-21 + - Add a "synthesis" section that provide clear explanation on how to implement it. + - Discriminate constraints concerning pre-conditions from constraints determined by the functional spec. Use different notation (e.g., $p$ and $q$). + - Modify the example on MC/DC to illustrate the handling of a functional constraint. + - Illustrate the approach on **Maxpool** + - Take care of tests cases that are actually identical due to absorption. +- [ ] Create a "guide" to help reading the informal spec. [project::[[SONNX]]] ➕ 2026-05-21 + - Introduce the notations, the compliance to IEEE + - Introduce the structure of the spec + - Explain how functional rules must be read (from top to bottom...). +- [ ] Check problem with negation when dealing with NaNs [project::[[SONNX]]] ➕ 2026-05-21 + - Less is specified as $A 2 + +#### 2025/04/16 +- Discussion about Jean-Loup's mail dated 10/04 "Re: SONNX Graph status" +- Discussion on the test strategy +- Validation of guidelines on formal spec +- Review of numerical accuracy +- Review of operators TBD +###### Minutes +- Points 1 and 2 addressed. +###### New actions +###### Previous actions +- [X] In the definition of tensor indexes in the glossary, add a range constraint of each index. +- [ ] In the examples, check that we use consistent notations for tensors (use of square brackets) +- [X] In the guidelines and templates, propose to use tags that are not visible in the rendered page + - This prevents cluttering of the spec. and tags can still be processed on the markdown source file. + - Proposed solution: + ``` + This is a first traceable specification item. This is another traceable specification item. This is some information. + ``` + - [X] Rollback to the previous version with visible tags. +- [ ] In the guidelines, give precise rules about the factorization of the spec. between types + - For structural operators, there is a unique spec for all types including "reals" + - [ ] Check if we could suppress "real" for structural operators. +- [ ] Add tests for the "special" values of tensor dimensions: scalar (rank 0) and null tensors +- [ ] Address the case of negative axes +- [ ] Check the behaviour of softmax for nb of axes > 2 + + +#### 2025/03/26 + +##### Agenda +- Finalization of **flatten**, **Clip** and **Tanh**. + +##### Minutes +- Brief discussion on the composite operators (operators built out of other operators). No clear statement as of today... +- Identification of an unexpected behaviour of **Clip** when using +0 and -0. **Clip(X,L,M)** is not equivalent to `min(M,max(X,L))`. For instance, `min(-0,max(-1,+0)) = -0` while, according to the specification, the value should be +0. +- Note that the case of +0 and -0 raise a problem when writing the expression `Y= f(...)` to specify the value of output `Y` : `f()` may return +0 or -0 indifferently (because +0 = -0) whereas the subsequent behaviour (based on `Y`) may be different. Instead of `=` we should use an arrow annotation to denote the assignment, not the equality (or we must write somewhere that `=` must be understood `symbol-wise`) + +###### New actions +- [ ] In the definition of tensor indexes in the glossary, add a range constraint of each index. +- [ ] In the examples, check that we use consistent notations for tensors (use of brackets) +- [ ] In the guidelines and templates, propose to use tages that are not visible in the rendered page + - Prevent cluttering of the spec. and tags can still be processed on the markdown source file. +- [ ] In the guidelines, give precise rules about the factorization of the spec. between types + - For structural operators, there is a unique spec for all types including "reals" + - [ ] Check if we could suppress "real" for structural operators. +- [ ] Add tests for the "special" values of tensor dimensions: scalar (rank 0) and null tensors +###### Previous actions +- [ ] Address the case of negative axes +- [ ] Check the behaviour of softmax for nb of axes > 2 + +#### 2025/03/13 + +##### Agenda + +##### Minutes +- Back on **MaxPool**: do we expect null tensor when preconditions are not satisfied? Do we consider that pre-conditions are necessarily satisfied? + - We consider that incompatible kernel / argument sizes leads to a null tensor. + - We keep the current organization where pre-conditions are expressed in the section dedicated to parameters and attributes, and provide an explanation of the expected behaviour in the "Error condition" section. + - In the most frequent case, we will simply state that the behaviour of the operator is not defined if any of the pre-conditions is not satisfied (see **Clip**). In some specific cases (e.g., the case of **MaxPool** and **Conv**, when the dimensions of the kernel and tensors are not compatible), we indicate that we return a null tensor. + - Why? Because it is not always trivial to verify that the condition is satisfied if dynamic shape modifications are performed. More often than not, conditions can be checked statically when all parameters related to sizes can be computed in advance. + - [X] Add a paragraph about constraint violation on the "informal spec guidelines". + - Eric: done +- Segregation of "specifying elements" from "non specifying elements". + - There are two objectives: + - discriminate the specifying parts from the documentation parts + - provide "hooks" for traceability purposes + - We propose to + - use section names when traceability is a the scale of the section. + - introduce tags `[specx.y...]` and `[/specx.y...]`, where `x`, `y`,... are numbers, when a specific element in the section needs to be designated. The dot notation (`x.y...`) is introduced when a more specific element needs to be identified within an element that is already tagged. + The exact tag is : `[spec1]
` + - Elements that are provided for information are tagged using `[info]` `[/info]`. All other elements are specifying. See the spec of operator [**Div**](../../../sonnx/ops/spec/informal/div/div.md). + - [X] Add a paragraph about tagging in guidelines + - Eric: done + - [ ] Address the case of negative axes + - [ ] Check the behaviour of softmax for nb of axes > 2 +- Review of **SoftMax** + +#### 2025/02/27 + +##### Agenda +- Review of actions +- Operators: [MaxPool](../../../sonnx/ops/spec/informal/maxpool/maxpool.md) and [Pow](../../../sonnx/ops/spec/informal/pow/pow.md). +- Accuracy: check the accuracy analysis of [div](../../../sonnx/ops/spec/informal/div/div_acc.md) and [tanh](../../../sonnx/ops/spec/informal/tanh/tanh_acc.md). See Franck's mail dated 2026/02/13. +- (Re-)discussion about using composition when defining complex operator. The case of **Conv**. +- Testing + - Shouldn't our reference implementation be using multiple precision (e.g., mpfr)? + +##### Minutes +- Reviewed: **Maxpool**, **Where**, **Pow** +- About **Pow** + - The specification currently imposes no restriction and goes beyond what is required by IEEE. +- [ ] Review **Pow** against IEEE 754 and restrict **Pow** to match IEEE's spec of Pow. +- About formal specification and verification + - Might be wise to limit format specification and proof to "complex" operators (and the graph semantics), especially the structural ones that make complex (hence error prone) manipulations of indexes. +- [ ] Propose a restriction to the scope of formal specification and verification (at least in a first phase) + + +#### 2025/02/13 +##### Agenda +No agenda defined... + +##### Minutes +- Travail sur **LeakyRelu**, **Max**, **Sigmoid**, **Sqrt**, **Exp**, **Log** + +- [X] Complete the disclaimer about tests and place emphasis on the last sentence (the rest being more methodological) + - (Eric) Done. +- [X In the set of "basic mathematical operators" that , remove the inverse trigonometric functions + - (Eric) Done. +- [ ] Check if, in the real domain, we use "$=\pm \infty$" without saying that it is a limit. +- [ ] Check that the sections of the spec are always ordered as follows: Real, Float, Int +- [ ] Introduce broadcasting for all operators that support it. + - (Eric) The other solution is to specify what is broadcasting and simply indicate, for a given operator, if it supports broadcasting or not. The effect of broadcasting on the operator can be explained once for all (see e.g., what has been done for **Max**). +- [ ] Checks that all hyperlinks are relative (i.e., they shall not point to the current repo / branch) +- [X] Introduce the IEEE special values in the definition section. In particular, define "+0", "-0" + - (Eric) Done. + - I think that a brief section about FP number would be worthwhile (with appropriate references to the standard and to the well-known "What Every Computer Scientist...") ) +- [X] When 0 is actually "+0", use "+0". "0" must be used either as the usual 0, or as "+0" or "-0". + - (eric) Added in [notations](../../../sonnx/ops/spec/informal/common/notations.md). +- [ ] Check the behaviour of **Max** for NaNs +- [ ] Check **Max** for "-0". +- [ ] Check all integer operators against overflow conditions +- [X] In the definition section, introduce the constants `minfloat16`, `maxfloat16`, etc. that are used (e.g.,) in the specification of **Exp**. + - (Eric) Added in [definitions](../../../sonnx/ops/spec/informal/common/definitions.md). + + +#### 2025/02/04 +- Feedback on strange observations on $MaxPool$ and code analysis. (30 min max) +- Proposal of guidelines modifications to factorize restrictions and constraints. +- Discussion (1h max) about the specification of ops in FP. + - What do we need exactly? + - Specification *vs.* implementation... Up to what point shall we be prescriptive wrt the implementation? + - Paper pointed out by Jean-Loup "Make it Real: Effective Floating Point Reasoning via Exact Arithmetic" + - The question of accuracy... +- Review of [Max](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/max/max.md), [Range](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/range/range.md), [Div](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/div/div.md)? + +#### Minutes + +##### Updates to the guidelines +- The section about attributes must be described only once, in the subsection about reals. The other sections (for the other types) must simply refer to it and express other specific constraint is necessary. +- The same applies to constraints: as far as they are not specific to a certain data type, they will not be restated, but simply refered to by an hyperlink. See the example of $Maxpool$. +- About constraints : + - We add a line about the "general constraints" for all operators. We do not specify precisely which of those constraints are actually applicable. This is up to the user to check... +- [X] Include factorizations of attributes and constraints and the sentence about general constraints in the guidelines and template + - Added in the guidelines (Eric) + +##### Update to preamble / lexicon etc. +- Introduce a "preamble" / "introduction" that will express some important general information and disclaimers + - Tests are provided to support the validation of the specification and the verification of a given implementation against the reference implementation. However: + - They only cover functional tests (they do not cover implementation) + - They are not aimed at being exhaustive and do not substitute to applicable practices in the domain of use (e.g., aeronautics...) + +##### Mathematical operators +- The specification is defined using the operators in R (in particular). +- A specific implementation (the one that we will actually generate) may be described in the "Accuracy" section in order to support the accuracy analysis. In that case, the operators will not be those in R but thos ein F. So we will use (e.g., "+." to express the addition in F). +- The specification will be written using a certain set of "basic" operator that will nt be defined any further. This includes at least `+`, `-`, `*`, `/`, `exp`, `sin`, etc. They have to be defined in the "preamble" or "introduction" to SONNX. +- [X] Define the set of basic operators in the SONNX introduction + - Added in the guidelines (Eric) +- The operator shall be specified for all special values (infs, +/-0, and NaN). + - This may be expressed using "if" clauses that will specify the behavior for those specific values, or using some general, informal sentence covering thoses cases. The point is that the behaviour of the operator must be covered for all these special values. +- [X] Check if infs ($\pm \infty$) belong to set the of real numbers + - Infinities are **not** real numbers (simply R is a field, an infs do not satisfy the law of fields, think about the addition of infinities). $R$ only contains finite numbers (excluding infinities). Infinities are introduced in the "extended real number system". See [here]([Rudin W. Principles of Mathematical Analysis 3ed](https://david92jackson.neocities.org/images/Principles_of_Mathematical_Analysis-Rudin.pdf)). +- (off meeting : if it is the case, then we will have to define what +inf (- | / ) -inf means exactly in R...)... +- [ ] In the section about accuracy, clarify the purpose of the analysis (methodological) +- [X] In the examples, use $\approx$ instead of $=$ when applicable + - Added in the guidelines (eric) +- [X] The jupyter notebook used to compute the examples must be provided + - Added in the guidelines (Eric) + +##### Tests +- We need a clear functional testing strategy. We cannot only rely to the tests automatically generated by Hypotheses. We may provide guidance on a "per operator class" basis. For instance, we may give specific guidance for operators using padding, etc. +- [ ] Elaborate a clear testing strategy +- The main test oracle is ORT. However, in some cases (e.g., MaxPool) the behaviour of ORT may be different from what we would expect (example: effect of inf in inputs). In that case, if the behaviour is not a manifest error (such as the case of "-4)...), we will add a notice explaining the discrepancy between our spec and the observable behaviour of ORT. + - Notice added (Eric) +- [X] Move the existing tests in the test folder +- [ ] (Henri) Check the behaviour of MaxPool with ORT on a GPU executor. + +##### Error conditions +- An "error condition" is a condition for which an operator can not compute a value that maps to some real counterpart (i.e., excluding Infs and NaN). It shall not cover the case where the output is not a value due to a simple propagation (of NaN, of Inf...). +- basically, we must put in the "error conditions" all results that are, , "not expected". +- The name of this section could possibly be changed: "Special cases" ? + +#### 2025/01/15 +##### Agenda +- Case of max:min/maxpool with NaN: for all operators, try to rely on the spec of Max and Min. (Nota: I don't like the spec NaN > Inf > ... I think that we should treat NaN as a special case because NaN is no normally comparable and here we are defining a comparison...) +- Processing of existing issues: see [issues tagged "TLSE WG"](https://github.com/users/ericjenn/projects/4/views/14)) + - Clip + - Relu +- Sujet pour CEtIC +- Interesting links identified by Jean-Loup in ONNX: + - https://onnx.ai/onnx/repo-docs/DimensionDenotation.html + - https://onnx.ai/onnx/repo-docs/TypeDenotation.html + - https://onnx.ai/onnx/repo-docs/MetadataProps.html +- Status of formal specification of CONV (Mariem) +- Discussion about SONNX event in late Feb / March +##### Minutes +- review of Clip +- review of Relu +- creation of Relu +- discussion about CETiC + +##### Actions +##### New actions + - [X] (1501-1, Jean-Baptiste) Faire une proposition de contenu pour une soumission à CETiC + - Proposition faite par JB le 16/01. + - Modifications proposées par Eric le <19/01> + - [X] (1501-2, Jean-Loup) Remove the relation NaN>Inf... and replace it by an explicit test for NaNs... + - [X] (1501-3, Eric) Introduce the term "Scalar" and (our) concept of "type" (numerical / value type) in the glossary. + - Done, see [here](../../../sonnx/ops/spec/informal/common/definitions.md). + - [X] (1501-4, eric Clarify the meaning of "heterogeneous" in ONNX + - ONNX, heterogeneous seems to mean that the inputs can be of different types. But this is not the case. For instance, operator `Where` has "heterogeneous" arguments but, in ORT, `Where` requires the same types for `X` and `Y`. For example, one cannot mix an int32 tensor with a float tensor, or an int32 tensor with an int64 tensor. There is no type promotion. + - Heterogeneous **seems** to mean that the operator have arguments of different type: boolean for B and other for `X` and `Y`. + - But this is not the case for operator `Add` that is also tagged `heterogeneous` even though all arguments must be of the same generic type `T` +(`Add(float,int)` is rejected and so is `(int32,int64)`). + - Tag "Heterogeneous" is automatically generated by ONNX' documentation generator that is located in [onnx/docs/gen_doc.py](https://github.com/onnx/onnx/blob/main/onnx/defs/gen_doc.py). + - Normally, "heterogeneous" should only refers to variadic arguments that are not "homogeneous". The generation of "heterogeneous" appear in version ONNX 1.4.0. And it is always related to variadic arguments. The schema is generated by function `display_schema` which calls `generate_formal_parameter_tags`: + + ```python + def generate_formal_parameter_tags(formal_parameter: OpSchema.FormalParameter) -> str: + tags: list[str] = [] + if OpSchema.FormalParameterOption.Optional == formal_parameter.option: + tags = ["optional"] + elif OpSchema.FormalParameterOption.Variadic == formal_parameter.option: + if formal_parameter.is_homogeneous: + tags = ["variadic"] + else: + tags = ["variadic", "heterogeneous"] + differentiable: OpSchema.DifferentiationCategory = ( + OpSchema.DifferentiationCategory.Differentiable + ) + non_differentiable: OpSchema.DifferentiationCategory = ( + OpSchema.DifferentiationCategory.NonDifferentiable + ) + if differentiable == formal_parameter.differentiation_category: + tags.append("differentiable") + elif non_differentiable == formal_parameter.differentiation_category: + tags.append("non-differentiable") + + return "" if len(tags) == 0 else " (" + ", ".join(tags) + ")" + ``` + + - By the way, even if the test was not only done for variadic argumebnts, it depends on the `is_homogeneous` boolean that comes from the schema defined in the `defs.cc` files (for instance, for `Where`: see [here](https://github.com/onnx/onnx/blob/main/onnx/defs/tensor/defs.cc#L2865). For the `Where` operator, the schema is the following: + ``` + ONNX_OPERATOR_SET_SCHEMA( + Where, + 16, + OpSchema() + .SetDoc(GET_OP_DOC_STR(std::string(Where_ver16_doc) + GenerateBroadcastingDocMul())) + .Input( + 0, + "condition", + "When True (nonzero), yield X, otherwise yield Y", + "B", + OpSchema::Single, + true, // <= This is the "homogeneous" boolean + 1, + OpSchema::NonDifferentiable) + .Input( + 1, + "X", + "values selected at indices where condition is True", + "T", + OpSchema::Single, + true, + 1, + OpSchema::Differentiable) + .Input( + 2, + "Y", + "values selected at indices where condition is False", + "T", + OpSchema::Single, + true, + 1, + OpSchema::Differentiable) + .Output( + 0, + "output", + "Tensor of shape equal to the broadcasted shape of condition, X, and Y.", + "T", + OpSchema::Single, + true, + 1, + OpSchema::Differentiable) + .TypeConstraint("B", {"tensor(bool)"}, "Constrain to boolean tensors.") + .TypeConstraint( + "T", + OpSchema::all_tensor_types_ir4(), + "Constrain input and output types to all tensor types (including bfloat).") + .TypeAndShapeInferenceFunction([](InferenceContext& ctx) { + propagateElemTypeFromInputToOutput(ctx, 1, 0); + if (hasNInputShapes(ctx, 3)) { + std::vector shapes; + shapes.push_back(&ctx.getInputType(0)->tensor_type().shape()); + shapes.push_back(&ctx.getInputType(1)->tensor_type().shape()); + shapes.push_back(&ctx.getInputType(2)->tensor_type().shape()); + multidirectionalBroadcastShapeInference( + shapes, *ctx.getOutputType(0)->mutable_tensor_type()->mutable_shape()); + } + })); + ``` + + - As all boolean values are "true", "heterogeneous" should never be displayed!! + + - [ ] (1501-5, ???) Clarify how we handle value constraints for attributes + - [X] (1501-6,Eric) Check the actual behavior of Relu and LeakyRelu in ONNX. Check if alpha can be negative. + - Relu: input [-1.0, 0.0, 1.0, float("nan")] => [ 0. 0. 1. nan] + - Leaky relu: [-1.0, 0.0, 1.0, float("nan")] => [nan 0. 1. nan] with alpha = NaN, as expected. Alpha can be negative and the value for X<0 becomes positive. + - [ ] (1501-7,???) Update the informal spec guidelines (enforce usage of ONNX names and provision of denotation, use of generic types: "with int in (int8,int16,...)" ) + +##### Past actions +None. \ No newline at end of file diff --git a/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png b/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png new file mode 100644 index 00000000..c50dc33d Binary files /dev/null and b/safety-related-profile/meetings/Local WGs/Tlse/attachments/conv_informa_formal.png differ diff --git a/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md b/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md new file mode 100644 index 00000000..dfce2110 --- /dev/null +++ b/safety-related-profile/meetings/Other_meetings/2025-03-20-An-Er-Se-Je.md @@ -0,0 +1,50 @@ +### Object: discussion about 4 points raised by Andreas and Sebastian: + + 1) One if not the central meeting at Onnx is the SIG Operators meeting. It takes place every month. We should actively bring our points there to + a) Get a discussion about our thoughts, from the experts who know operators, know how the way would be to implement them. + b) We could also draw more attention to our initiative there. + + 2) The next Onnx Community Meetup will probably take place in June 2025. + (https://github.com/onnx/steering-committee/blob/main/meeting-notes/2025/20250305.md) + There we will have the opportunity to present what we are doing and what we have achieved. + + 3) I think we need to create a lot more issues at https://github.com/onnx/onnx in order to work out the concerns more clearly, or to be able to discuss them even more with the community there, or to be able to refer to them in the Operators Meeting. + + 4) As there are certainly already issues concerning SONNX at the moment, I could well imagine a new label “sonnx” for this, so that we can filter even better according to the topics. + +### Participants +Andreas, Sebastian, Jean, Eric + +### Main conclusions + +- We need to formalize issue processing and push the issues to ONNX + - Decisions: + - Issues reported by the WG member are proposed to the WG (use the existing [issues file](../../documents/issues.md)) + - Issues are discussed during the meeting to determine whether or not they must be pushed to ONNX with the sonnx tag + - If there is an agreement, an ONNX issue is created and tagged "sonnx" + - Should the issue deserves it (or should there be too many "sonnx" issues not processed by ONNX), we may request a time slot to the ONNX operators SIG. +- We need to present our work to ONNX + - Results to be presented to the ONNX meetup in June +- We need to make our work more visible + - (Push our work more regularly to ONNX...) + - (Update our landing page to point to the main results...) +- We need to decide how our results (operator spec, ref implementation, etc.) will actually be integrated in ONNX and become visible to the ONNX users. + - One possibility could be to add a SONNX tab to the ONNX operators page (see below). + - To be discussed wit the WG + +![alt text](./imgs/onnx_ops.png) + +- We need to be careful when putting restrictions on operators not to repel potential SONNX users. + - Restriction shall be justified. + - We have two main categories of restrictions: + - restrictions related to a specific requirement (see [specs](../../documents/reqs.md)) + - restrictions aimed at simplifying our work considering (i) the use case we target , (ii) the resource we have + - Restrictions in the second category may be relaxed upon demand, as long as someone (the demander?) does the specification, verification and implementation work + +### Actions + +- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. +- [ ] (2003-2, Eric) Update SONNX landing page to point to intersting material... +- [ ] (2003-3, Eric ) Initiate discussion in WG about ONNX integration and propose solutions to ONNX +- [ ] (2003-4, Eric ) Give an example of the two categories of restrictions + diff --git a/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf b/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf new file mode 100644 index 00000000..46291a15 Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/2025-10-24 Work on specification of graph execution semantics.pdf differ diff --git a/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf b/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf new file mode 100644 index 00000000..1d38eeae Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/POSTER_A0 MobiliT.pdf differ diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf new file mode 100644 index 00000000..7dfc6083 Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - ERTS2026.pdf differ diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf new file mode 100644 index 00000000..47ceb5f8 Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - WG114- oct-2025.pdf differ diff --git a/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf b/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf new file mode 100644 index 00000000..6f720d1f Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/SONNX - WG114.pdf differ diff --git a/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png b/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png new file mode 100644 index 00000000..558b0f9f Binary files /dev/null and b/safety-related-profile/meetings/Other_meetings/imgs/onnx_ops.png differ diff --git a/safety-related-profile/meetings/README.md b/safety-related-profile/meetings/README.md index 18573012..be6f0835 100644 --- a/safety-related-profile/meetings/README.md +++ b/safety-related-profile/meetings/README.md @@ -1,5 +1,5 @@ -Location of the SRP meeting minutes. +- [Minutes of the SONNX WG](./minutes.md) - Proposals for presentations during the WG meetings shall be done in [presentation proposals](https://github.com/ericjenn/working-groups/blob/3f64a7b8b5dc4d9214f3d69e4e3b7ec1523b56cc/safety-related-profile/meetings/presentation_proposals.md) - +- The slides of the presentations done during the WG meeting are located [here](./slides/) diff --git a/safety-related-profile/meetings/analysis_sub_wg/README.md b/safety-related-profile/meetings/analysis_sub_wg/README.md deleted file mode 100644 index 9b0d1b29..00000000 --- a/safety-related-profile/meetings/analysis_sub_wg/README.md +++ /dev/null @@ -1,2 +0,0 @@ -This is the working area for the ONNX analysis sub-wg. -Results of this activities can be found in file [issues.md](../../documents/issues.md). diff --git a/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md b/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md deleted file mode 100644 index 47d6670e..00000000 --- a/safety-related-profile/meetings/analysis_sub_wg/slides-18-12.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -marp: true -footer: '_2024-12-18 SONNX WG meeting_ - Eric' ---- -# SONNX -## ONNX analysis sub-wg #1 - ---- -# **Objectives** -- ---- -# **Method** ---- -# **Participants** ---- diff --git a/safety-related-profile/meetings/attachments/conv2d.c b/safety-related-profile/meetings/attachments/conv2d.c new file mode 100644 index 00000000..5e5dc8c2 --- /dev/null +++ b/safety-related-profile/meetings/attachments/conv2d.c @@ -0,0 +1,92 @@ +#include // Include necessary headers +#include // For rand() function + + +// Define constants +#define F 2 +#define OH 28 +#define OW 28 +#define IH 5 +#define IW 5 +#define C 3 +#define KH 3 +#define KW 3 +#define STRIDES 1 +#define DILATATION 1 +#define PAD_LEFT 0 +#define PAD_TOP 0 + +// Function prototype +void conv2D(const float* input, float* output, const float* kernel, const float* biases); + +int main() { + // Define input, output, kernel, and biases arrays + float input[IH * IW * C]; + float output[OH * OW * F]; + float kernel[KH * KW * C * F]; + float biases[F]; + + // Initialize input array with random values between 0 and 1 + for (int i = 0; i < IH * IW * C; i++) { + input[i] = (float)rand() / RAND_MAX; + } + + // Initialize kernel array with random values between -1 and 1 + for (int i = 0; i < KH * KW * C * F; i++) { + kernel[i] = ((float)rand() / RAND_MAX) * 2 - 1; + } + + // Initialize biases array with random values between -1 and 1 + for (int i = 0; i < F; i++) { + biases[i] = ((float)rand() / RAND_MAX) * 2 - 1; + } + + // Define and initialize output array + for (int i = 0; i < (IH - KH + 1) * (IW - KW + 1) * F; i++) { + output[i] = 0.0; + } + + // Call conv2D function + conv2D(input, output, kernel, biases); + + // Print the result + printf("Result:\n"); + for (int f = 0; f < F; f++) { + printf("Filter %d:\n", f); + for (int i = 0; i < OH; i++) { + for (int j = 0; j < OW; j++) { + printf("%f ", output[i * OW * F + j * F + f]); + } + printf("\n"); + } + } + + return 0; +} + + +// conv2D( +// input : f32[IH, IW, C] @DRAM, +// output : f32[OH, OW, F] @DRAM, +// kernel : f32[KH, KW, C, F] @DRAM, +// biases : f32[F] @DRAM +// ) +void conv2D( const float* input, float* output, const float* kernel, const float* biases ) { + for (size_t f = 0; f < F; f++) { + for (size_t i = 0; i < OH; i++) { + for (size_t j = 0; j < OW; j++) { + output[i * OW * F + j * F + f] = 0.0; + for (size_t c = 0; c < C; c++) { + for (size_t m = 0; m < KH; m++) { + for (size_t n = 0; n < KW; n++) { + if (0 <= i * STRIDES + m * DILATATION - PAD_LEFT && i * STRIDES + m * DILATATION - PAD_LEFT < IH && (0 <= j * STRIDES + n * DILATATION - PAD_TOP && j * STRIDES + n * DILATATION - PAD_TOP < IW)) { + output[i * OW * F + j * F + f] += input[(i * STRIDES + m * DILATATION - PAD_LEFT) * IW * C + (j * STRIDES + n * DILATATION - PAD_TOP) * C + c] * kernel[m * KW * C * F + n * C * F + c * F + f]; + } + } + } + } + output[i * OW * F + j * F + f] += biases[f]; + } + } + } +} diff --git a/safety-related-profile/meetings/attachments/date2026_workshop.pdf b/safety-related-profile/meetings/attachments/date2026_workshop.pdf new file mode 100644 index 00000000..3c33110b Binary files /dev/null and b/safety-related-profile/meetings/attachments/date2026_workshop.pdf differ diff --git a/safety-related-profile/meetings/attachments/image.png b/safety-related-profile/meetings/attachments/image.png new file mode 100644 index 00000000..50590d77 Binary files /dev/null and b/safety-related-profile/meetings/attachments/image.png differ diff --git a/safety-related-profile/meetings/attachments/why3_op_prio.png b/safety-related-profile/meetings/attachments/why3_op_prio.png new file mode 100644 index 00000000..9094f486 Binary files /dev/null and b/safety-related-profile/meetings/attachments/why3_op_prio.png differ diff --git a/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf b/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf deleted file mode 100644 index 155851e6..00000000 Binary files a/safety-related-profile/meetings/errors/2024-11-06 - Numerical issues.pdf and /dev/null differ diff --git a/safety-related-profile/meetings/errors/README.md b/safety-related-profile/meetings/errors/README.md deleted file mode 100644 index 46f93a09..00000000 --- a/safety-related-profile/meetings/errors/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This directory contains minutes of discussions dedicated to the question of numerical accuracy, specification of FP operators, etc. : -- A [few elements](01_what_is_the_issue.md) about the issue -- The [slides](slides-06-11.md) presented on 11/06 WG meeting (Eric) diff --git a/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md b/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md new file mode 100644 index 00000000..b5e6aad9 --- /dev/null +++ b/safety-related-profile/meetings/errror conditions/2025-07-30 - Discussions.md @@ -0,0 +1,75 @@ +See also the previous note [here](./error_conditions_2.md). + +- An operator in the SONNX set shall be fully specified. + - In particular, the domain and range of the operator must be fully defined. + - The expected value to be returned by the operator must be specified for all possible input value in the domain. + +- The domain must be specified by constraints on the inputs (e.g, "x > 0" for operator log(x)). + - For complex functions, providing an explicit definition of the domain may be difficult... For instance, what is the domain of definition of f(x,y)=tan(sqrt(x+y))? [well, thi sis not the most complicated case, but you probably get the point...] + +- Let's call "abstract" the mathematical operator. + - An abstract operator may be a *** partial function ***, i.e., a function that is not be defined for some values of the input. + +- Let's call "concrete" the actual, computer-based, implementation of an operator. + - Ideally, a concrete operator should be a *** total function ***, i.e., a function that *** always *** returns a value, even if the input value is not in the domain defined by the abstract function. + - For instance, the concrete Div(x,y) for the float32 data type shall return an Inf is y=0.0 whereas operator Div(x,y) is simply not defined in R. + - The SONNX specification shall specify the total function. + +- The principle of defining total functions is applicable for concrete operators in the domain of IEEE floating point numbers because this behaviour is completely supported by the current hardware targets. + +- This principle is *** not applicable *** to integers because there is no direct hardware support for that. + - This means that, for integer data types, there should be something such as an "undefined behaviour" and an "undefined output". + - Concerning "undefined behaviour" + - This occurs for instance in the case of a division by zero. The behaviour depends on the target (including HW, runtime, OS). For instance, a trap may be raised by the processor and handled by the OS, the runtime, etc. From a SONNX user perspective, this means that the operator never returns. + - We *** could *** specify an operator with a controlled behaviour, but + - (i) this would certainly require modifying the operator interface to carry the information that the operator failed (since this data cannot be carried in the integer value itself, as it is the case for floats). + - (ii) no implementation would be able to comply with it (and, in particular, no existing implementation would) because testing the condition of occurence of the error would be prohitively costly. + - Concerning "undefined value" + - In the case of wrap-arounds, the error may simply be undetected and propagate. Back in the days, there used to be traps for wrap-around conditions, but it is no more the case today: to detect a wrap-around, there shall be some code to test the condition (there might be some exceptions, but I think that this is the most general case). + - SONNX could require wrap-around conditions to be detected and processed. If detected, this error would be handled in the same way as division by zero (the operator simply does not return). But this is practically unfeasible since it would require testing the condition bits after every operator that may lead to a wraparound. + - So, the specification must indicate that an operator may return an "undefined value" when a wrap-around condition may occur. + - Another solution would be to specifiy the exact behaviour of the integer operators, *** including the wrap-around ***. In that case, there would be no "undefined value", but the specification would be slightly more complicated ("+" on signed ints would be specified in 2's complement arithmetic. See "Option 2" in my note. + +*[Edoardo]* I strongly favour the "undefined value" option. Specifying the behaviour of integer overflow would require specifying details about the implementation of operators (e.g. the order of every step of a matrix multiplication). +> [Eric] I agree. To be discussed during meeting. +> +- So, + - For IEEE data types + - operators *** must *** be total functions, which means that we have to cover all cases, including those where inputs are Infs and NaNs and where operator may return Infs and NaNs. The specification shall indicate what is the value returned by the operator when the inputs contains Infs or NaNs. + - For integer data types, + - for wrap-around : we fully specify operators with wrap around, i.e., see "Option 2" in this note or we specify that operations may wrap around and return "undefined value" + - for div / 0 : we specify that some "undefined behaviour" may occur. + +- The specification shall not refer to or impose a specific implementation. If the specification uses an operational way (a series of computations) to compute the function, it does not imply that the function will actually be implemented that way. + +- We have to discriminate errors that are inherent to the function (i.e., that are part of the mathematical definition of the function, so they shall appear in the definition of the abstract operator) from errors that depend on the way the function is implemented. + - For instance, let's consider the SoftMax function. The abstract function is fully defined over ]-\intfy, +\infty[. There is no constraint inherent to the function. + - The concrete SoftMax for float32 should return the correct approximate value of the abstract SoftMax for any value between FLOAT_MIN and FLOAT_MAX. The specification shall also specify what is the expected output when inputs contains Inf or NaNs. + - In this very case, this specification is "feasible" (i.e., there is a simple way to comply with it): one has simply to implement SoftMax using the "-max(zi)" trick. This means that it is reasonable to specify it that way. + - In other cases, preventing error cases at the implementation level is much more difficult. + - For instance, preventing an overflow or even a NaN in a matrix multiplication is not (easily) feasible, which means that we cannot write that the MatMul should return the correct approximate value of the abstract MatMul for any value between FLOAT_MIN and FLOAT_MAX. + - This can only be required for a specific domain where we know that this property can be achieved "simply" (whatever that means...). + +- The SONXX specification may specify a "minimum" level of service". For instance, operator Op return f(x) for x in D and f(x) or NaN for x out of D. + - A specific implementation may provide a better "service" such as "operator Op returns f(x) for any x". See the example of Softmax given by Franck. + - In that case, SONNX would require the implementer to provide an updated version of the specification of Op corresponding to its own implementation. However, the operator will at least comply with the SONNX specification. + +*[Edoardo]* I am not against SONNX being a "minimum level of service" specification, but it would be good to agree on a reference level. Do we analyse existing implementations and decide what the minimum threshold is? E.g. I expect the convolution operator to fail (Inf/NaN) for different input domains depending on whether the implementation is naive (nested loops) or optimised (FFT, Winograd, etc). One option is to base our specification of valid input domain D on the naive implementation produced by Why3, however bad it is (e.g. it will probably not contain the "-max(zi)" trick for SoftMax). + +>[Eric] To be discussed. Note, howver, that the Why3-generated implementation will have to comply with its formal sepcification, which means that the "valid input domain" will have to be specified as a predcondition on the arguments. + +- The semantics of a SONNX model will be the one derived from the SONNX specification of operators, not the one considering the "improvements" done in some specific implementation. So, even if this approach makes sense, it cannot really be part of SONNX... + +*[Edoardo]* Regarding the integer overflow issue, just bear in mind that not all hardware platforms deal with overflow by wrap-around. Instead, some Digital Signal Processors (DSP) may favour saturation, i.e. INT_MAX + 1 == INT_MAX and INT_MIN - 1 == INT_MIN. Specifying overflow as undefined behaviour in SONNX will cover both cases. + +>[Eric] You are absolutely right and this is a good point because we have already deployed IA on TI's Jacinto target which hosts C6X and C7X DSPs... It also raises the question of fixed point computations (but thi s is yet another story). + +*[Edoardo - correction]* The two comments below are about approximation error, i.e. the expected return value of operators for inputs in the valid domain. Regarding failure modes instead, I only noticed a little discrepancy in the division-by-zero example. In 2025-07-30 - Discussions.md we say that Div(x,y) returns Inf when y=0.0. That is true in IEEE754 floating point only if x!=0.0. In error_specification.md we say that "x/y returns "NaN" when y = 0" (see IEEE floating point values (FP16, FP32, FP64) section). + +>[Eric] You are right, 0.0/0.0 returns Nan. + +*[Edoardo]* Regarding the floating-point behaviour, there are two opposite philosophies. The IEEE754 standard recommends implementing correctly-rounded operators. That is, the operator behaves as if it was computing the result in infinite precision and then rounding to the nearest flaoting-point value. In contrast, the C standard leaves room to implementation-dependent behaviour which, in the extreme case, could mean that log(x) == 42 for any x is a valid C operator. Interestingly, the upcoming C standard plans to introduce "correctly rounded" version of mathematical operators with a prefix "cr_". That is, log(x) is implementation dependent, but cr_log(x) must be correctly rounded. + +*[Edoardo]* Implementing a correctly-rounded operator is quite hard. Even more so if the operator is a function of many variables. Restricting the domain would not mitigate this issue. Hence, the biggest problem I see does not lie in specifying a restricted domain (e.g. Sigmoid(x) should be correctly-rounded for -4 <= x <= +4), but specifying a "softer" requirement than correct roundedness (e.g. the relative error produced by Sigmoid(x) should not exceed 2%). In this respect, I suggest considering bounds on the maximum relative and absolute error allowed. A thorough empirical analysis of existing float32 and float64 libraries might give an indication on how to fix reasonable bounds on the numerical error. + +> [Eric] As you noticed, we are dealing here with "error conditions" (or "failure modes"), not approximations. For approximations, Francks has already proposed an approach (see the [Guidelines](../../documents/profile_opset/guidelines.md)). diff --git a/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf b/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf new file mode 100644 index 00000000..e4620903 Binary files /dev/null and b/safety-related-profile/meetings/errror conditions/2025-07-30-SONNX_error.pdf differ diff --git a/safety-related-profile/meetings/errror conditions/error_conditions_2.md b/safety-related-profile/meetings/errror conditions/error_conditions_2.md new file mode 100644 index 00000000..8ce3a15e --- /dev/null +++ b/safety-related-profile/meetings/errror conditions/error_conditions_2.md @@ -0,0 +1,78 @@ +Error conditions and runtime errors +(Material discussed on 2025-07-16) + +## Case 1: floating point computation + +### General principles +- No exception is raised during a floating point computation. +- Error values occur and are propagated according to the IEEE 754 standard: + +### Example : Softmax + +`Softmax(input,axis)= Exp(input) / ReduceSum(Exp(Input), axis=axis, keepdims=1)` + +$$s(z_i) = {e^{z_i} \over \sum_{j=1}^K e^{z_j}} $$ + +Described using `Exp` (exponential element wise) and `ReduceSum` (computes the sum of the input tensor’s elements along the provided axes). + +See the [Google Collab notebook](https://colab.research.google.com/drive/1fFDuuAK_lA3ScDk6I_VplbC5RYBncQD6#scrollTo=BfheCRKub5vl) + +Neither the ORT nor the Reference over or underflow. + +The ONNX reference implementation computes SoftMax as follows: +class Softmax(OpRunUnaryNum): + +```python + def _run(self, X, axis=None): + axis = axis or self.axis + tmp = X - X.max(axis=axis, keepdims=1) + Y = np.exp(tmp) + Y /= Y.sum(axis=axis, keepdims=1) + return (Y.astype(X.dtype),) +``` + +The operation is not implemented in the naive way exactly the one specified. + +If we use the naive, direct implementation: +```python +output_data_underflow_naive = np.exp(input_data_underflow) +output_data_underflow_naive /= output_data_underflow_naive.sum(axis=0, keepdims=1) +``` +we have a nan. + +There can't be a nan for Onnxruntime thanks to normalization, but another implementation could under/overflow. + +Shall we simply write : + +>**Runtime errors** +>Operator `SoftMax` may overflow (resp. underflow) for large (resp. small) input input values. +> In that case, the operator will return a nan. + +## Case 2: Integer computations + +### Example 1: `add` over int32 values + +**Option 1**: complete the specification with preconditions +- Operator `add` is specified using the standard $+$ operator, i.e, add(x,y) returns $x+y$ and + - a precondition is added: $$-2^{31} \leq x+y \leq 2^{31}-1 $$ + - or conservative constraints on $x$ and $y$ are added + - If $x > 0$ and $y>0$ then $y ≤ (2^{31}-1) -x$ + - If $x < 0$ and $y<0$ then $y ≥ -2^{31} - x$ + + - those conditions could be checked by the user by adding a dedicated sub-graph. + +**Option 2:** give a rigorous specification of the `add` operator + +$$z=\left\{ + \begin{array}{ c l } + x + y - 2^{32} & \quad \textrm{if } x + y > 2^{31}-1 \\ + x + y + 2^{32} & \quad \textrm{if } x + y < -2^{31} \\ + x+y & \quad \textrm{otherwise} + \end{array} +\right.$$ + +**Option 3** +>**Runtime errors** +> Operator `add`may overflow in the positive or negative numbers for large positive of negative input values. +> In that case the result returned by the operator does not comply with the specification. + diff --git a/safety-related-profile/meetings/errors/error_specification.md b/safety-related-profile/meetings/errror conditions/error_specification.md similarity index 53% rename from safety-related-profile/meetings/errors/error_specification.md rename to safety-related-profile/meetings/errror conditions/error_specification.md index fea19721..6cfc21bc 100644 --- a/safety-related-profile/meetings/errors/error_specification.md +++ b/safety-related-profile/meetings/errror conditions/error_specification.md @@ -15,13 +15,15 @@ Déf. : a constraint that is expressed in the specification as a relation involv Those constraints are pre-conditions for the operator. For this reason they don't involve outputs. Note that outputs aren't considered to be potentially part of the precondition. The cases where we want the shape of the output to be explicitly given by the user (and not "inferred") shall be managed using attributes. -** This is to be discussed...** +**To be discussed...** ### A.1 - Statically verifiable constraints -Those constraints may depend on tensor's values, but those tensors are constants given in the ONNX file (e.g., biases, weights, etc.) + +Those constraints may depend on values, but those values are known before runtime (e.g., biases, weights, etc.). They are stored in the ONNX file or in some external file. ### A.2 - Non-statically verifiable constraints -Those constraints cannot be checked on the ONNX file because they depend on the values of non-constant tensors, i.e. infered tensors. This is the case for simple operators such as "log" (input values must be strictly positive) or for more complicated operators such as "unsqueeze" (input "axes" must not contain duplicate values). + +Those constraints cannot be checked before execution because they depend on the values of non-constant inputs. This is the case for simple operators such as "log" (input values must be strictly positive) or for more complicated operators such as "unsqueeze" (input "axes" must not contain duplicate values). ## B. - Non expressed constraints @@ -35,47 +37,51 @@ Déf. : A constraint that is not expressed in the specification, because definin - Case B : Error conditions cannot be checked neither the ONNX file nor on the (inputs,outputs,attributes). Those errors can be checked by instrumenting the code (in the previous example, by checking that f(y) is not zero before doing the division), by hardware error handling mechanims (traps) or by using singular values (NaN, inf, etc.). -## Recommandations - -### All cases - -- The specification of operators shall state that the behaviour of the operator is undefined should any constraints (in A or B) be violated. +## Recommendations -### Case A.1 +### Case A.1 (Statically verifiable constraints) - A model that violates constraints in class A.1 for at least one of its operators is an **invalid model**. - An **invalid model** can be detected by a "model checker" - Those constraints must be clearly identified because they will constitute the specification for the model checker. -### Case A.2 -- Option 1: - - The error condition is specified and the behaviour (incl. output) in case of error is precisely defined. -- Option 2: - - The error condition is specified but the behaviour (incl. output) in case of error is not specified. The specification only states that the behaviour is undefined. +### Case A.2 (Non statically verifiable constraints) +The error condition is specified. +The behaviour in case of error is specified. + +#### At operator level +For instance, considering the `div` operator: +- Error condition: "denominator equals 0" +- Behaviour + - For real values, operation is "undefined". + - For FP64, FP32, FP16, BFLOAT16, behaviour is "returns 'NaN'". + - For INT8, behaviour is "undefined". -Note that this may be part of the specification of the graph execution, not only the operator. It may also involves the Operational Design Domain (ODD) of the model. +Note the difference between "operation is undefined" and "behaviour is undefined". -### Case B +#### At graph level +The only thing to be specified is how "inf" and "nan" propagate. -The specification shall indicate that an error may occur during computations even though we may not be able to describe precisely the conditions at the (inputs and attributes) level. +### Case B (Non expressed constraints) + +The specification shall indicate that an error may occur during computation even though we may not be able to describe precisely the conditions at the (inputs and attributes) level. The low-level error condition (e.g., "division by zero") must be described. - Option 1: - - The low-level error condition is specified and the behaviour (incl. output) in case of error is precisely defined. + - The low-level error condition is specified and the behaviour in case of error is precisely defined. This covers the case where `inf` and `nan` may be generated and propagated. - Option 2: - - The error condition is specified but the behaviour (incl. output) in case of error is not specified. The specification only states that the behaviour is undefined. + - The error condition is specified but the behaviour in case of error is not specified. The specification only states that the behaviour is undefined. We consider the **two options** because checking the occurrence of the low-level error condition may be more or less difficult to detect. For instance, to detect that no overflow occur when adding integer numbers would require specific code on most architectures... +# Miscellaneous Remarks -# Remarks - -### Remark #1: Restricting SONNX to Explicit Constraints +## Remark #1: Restricting SONNX to Explicit Constraints The impact of restricting the scope of SONNX to operators whose domain of definition corresponds to explicit constraints on inputs and attributes on its expressiveness should be assessed. -### Remark #2: Domains and Ranges for Operators +## Remark #2: Domains and Ranges for Operators For an operator, one can associate not only a domain for its inputs but also a range for its outputs. For example, the sigmoid function has a range: $\text{range} = [0, 1]$ @@ -84,4 +90,44 @@ By defining both domains and ranges, it becomes possible to impose generic cons - The ODD of the model should be shuch that inputs of the model must necessarily fall within the domain of the operators in the corresponding first layer. - The range of an operator upstream of another operator must be included in the domain of the latter. -These constraints lead to graph-level requirements aimed at ensuring error-free inference in the model. \ No newline at end of file +These constraints lead to graph-level requirements aimed at ensuring error-free inference in the model. + +## Remark #3: Handling of NaNs and INFs + +Three cases must be considered : +- real values +- floating point values + - IEEE floating point values (FP16, FP32, FP64) + - Non IEEE floating point values (e.g., BFLOAT16, FP8, etc.) +- integers values + + +### Real values + +For real values, we will simply indicate that the operation is not defined when the values are out of range. This will be expresses as constraint on the input parameters, attributes. + +### Floating point values + +#### IEEE floating point values (FP16, FP32, FP64) +For IEEE floating point value, we will simply indicate that the IEEE 754 rules applies, e.g., x/y returns "NaN" when y = 0. Note that this makes sense since Python follows IEEE. +This means that, e.g., a division by zero will lead to a NaN that will propagate in the rest of the computation. +This value can be checked at the output level. + +Note that NaN can be detected in the hidden layers using the [IsNaN](https://onnx.ai/onnx/operators/onnx__IsNaN.html) ONNX operator. And, of course, they can be detected at the output. + +So, in the standard, we may simply recall that the IEEE 754 applies and specify what the operator is expected to generate in case when a parameter is out of range. + +#### Non IEEE floating point values (e.g., BFLOAT16, FP8, etc.) + +For BFloat16, the IEEE754 rules should apply for INF and NaNs. So we fall back to case + +As of today, ONNX does not support FP8... + + +#### Integer values + +For integers values, two cases : +- division by zero conditions, which lead to a trap and a "core dump" (in the best case) +- overflow conditions, which are not detected + +(I propose to postpone the case of integer values for a while.) diff --git "a/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md" "b/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md" new file mode 100644 index 00000000..6f0c428f --- /dev/null +++ "b/safety-related-profile/meetings/formal_methods/Meeting_Salom\303\251_Mariem.md" @@ -0,0 +1,100 @@ +# Compte rendu de réunion — Avancement sur le type `scalar` et génération du code C + +**Date :** 24/07/2025 +**Participants :** Salomé, Mariem +**Sujet :** Avancement sur le type `scalar` et génération du code C + +--- + +## 1. Points discutés + +Salomé a résumé ce qu'elle a fait pendant les deux dernières semaines. Elle a : + +1. Vérifié que l’opération d’arrondi utilisée dans ONNXRuntime est la même que celle utilisée dans Why3, à savoir **RNE**. +2. Cloné le type `scalar` générique pour définir les types `ScalarInt32` et `ScalarFloat32`, en utilisant les bibliothèques `mach.int.Int32` et `ieee_float.Float32`. +3. Implémenté l’opérateur générique `add` prenant un type `t` en paramètre. +4. Cloné l’opérateur générique `add` pour définir les modules `OpAdd_Float32` et `OpAdd_Int32`. +5. Généré le code OCaml. +6. Créé les fichiers de tests et testé le comportement de l’opérateur `add` avec `int32` et `float32`, dans les cas d’addition avec et sans débordement. +7. Réussi à générer un code C à partir de l’implémentation naïve du type `scalar`, mais la génération est incomplète : seule la structure de données `scalar` est traduite en C. Des problèmes sont rencontrés lors de la traduction de l’opérateur `add`. + +--- + +## 2. Problématiques identifiées + +- Le test de l’opérateur `add` avec des `float32` en cas de débordement ne donne pas le même résultat que ONNX, car le type `Float32` n’existe pas dans OCaml (seul le type `float64` y est disponible). + **Solutions possibles :** + - Définir `float32` en OCaml. + - Générer du code C pour les tests. + +- Certaines expressions en WhyML ne peuvent pas se traduire en C, comme : `match ... with`. +- La structure utilisée pour définir `scalar` est un peu complexe ; elle pourrait être simplifiée afin de faciliter la génération du code C. + Une autre solution possible : ne générer en C que les types problématiques en OCaml (notamment `float32`), pour éviter de devoir traduire le type générique complet. + +--- + +## 3. Actions à venir + +- Enrichir le type `scalar` avec des opérations logiques et des preuves, car l’implémentation actuelle ne contient que des opérations fonctionnelles. +- Optimiser la structure du type `scalar` générique traduite en C (à discuter avec l’équipe et Loïc). +- Tenter de générer du code C à partir de l’implémentation **non naïve** du type `scalar`. + + +--- + + +# Compte rendu de réunion — Avancement sur le type `scalar` + +**Date :** 03/07/2025 +**Participants :** Salomé, Mariem +**Sujet :** Avancement sur le type `scalar` + +--- + +## 1. Points discutés + +### Test de l'opérateur `add` (int32 et float32) + +- Salomé a implémenté un test de l'opérateur `add` en utilisant la librairie `ieee_int32` de Why3. +- La création du **driver `type.drv`** a été complexe et a demandé un temps significatif. +- À l’avenir, **solliciter Loïc**, qui dispose de **drivers déjà implémentés** pouvant être réutilisés. + +### Modifications des fichiers OCaml + +- Les fichiers `tensor.ml` et `tensor.mli` ont été modifiés pour : + - appeler la fonction à tester (`add`), + - afficher le résultat pour validation. + +### Création du fichier de test OCaml + +- Les fichiers `test_add.ml` et `test_add.expected` ont été générés pour : + - réaliser des cas simples de test des types `int32` ou `float32`, + - en exploitant les fonctions des fichiers `tensor.ml` et `tensor.mli`. + +--- + +## 2. Problématiques identifiées + +### Limitation des types numériques en OCaml + +- OCaml ne propose que les types `int`, lié à l'architecture, et `int32`. +- Il n'existe pas de prise en charge native pour des types comme `int8` ou `float32`, nécessaires à nos tests. + +### Comportement de l’arrondi dans Why3 vs ONNX + +- Why3 utilise un **arrondi RNE (Round to Nearest Even)** pour les opérations `float32`. +- Il est nécessaire de **vérifier si cet arrondi correspond** à celui défini dans ONNX. +- Il faudra **étudier le code source de l’opérateur `add` dans ONNX** pour : + - identifier les **opérations à redéfinir** dans `scalar`, + - et celles **importables directement** depuis Why3. + +--- + +## 3. Actions à venir + +- Vérifier le comportement d’arrondi dans ONNX (comparé à RNE de Why3). +- Déterminer quelles opérations seront : + - redéfinies dans le type `scalar`, + - ou importées depuis les bibliothèques Why3. +- Formaliser les éléments **spécifiés informellement par Franck** dans la **spécification du type `scalar`**. + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md b/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md new file mode 100644 index 00000000..e79ae50c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/README.md @@ -0,0 +1 @@ +Code written during the 2025/03/19 workshop \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore new file mode 100644 index 00000000..757f50e2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.gitignore @@ -0,0 +1,6 @@ +why3session.xml* +why3shapes.gz* +/.why3find +/_build +/html +/lib/*.bak diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6 new file mode 100644 index 00000000..605e95ef --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/00/009a248114cf07ccc6af2c142b6a16a6 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.016775 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7 new file mode 100644 index 00000000..6560d664 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/07/07aa782e56cc00fb1b5ae89a393206b7 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.026858 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e new file mode 100644 index 00000000..7c3fc348 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/0f/0fd4a9535c71b47657ce94fd1ab0d79e @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.014509 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1 new file mode 100644 index 00000000..2c700d87 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/19/190cd40d582da3801bd6b6f13f2560a1 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.013517 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e new file mode 100644 index 00000000..3d2a93c7 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/21/210d6dc9289578c32fe22d55b6045e3e @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.038737 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f new file mode 100644 index 00000000..24f7d845 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/24/24807122cba2c06caa5e7c05e4b9013f @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.152687 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8 new file mode 100644 index 00000000..2ba0da9e --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/27/27dd260b3c70c3aa37d7ed68ff32d5c8 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.01358 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703 new file mode 100644 index 00000000..a64e4e28 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/2d/2d8711f0cd64e71e6b70554b36cc5703 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010714 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c new file mode 100644 index 00000000..cd5797b8 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/45/455b1f6c501467a80fa06ab091edf36c @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.00949 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f new file mode 100644 index 00000000..3d69ff75 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/49/49fa11f8873f2a6684f5a3bc078b7d6f @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.041714 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901 new file mode 100644 index 00000000..19043317 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5b/5ba3121e7f510036ba2d321112c60901 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010766 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102 new file mode 100644 index 00000000..43fc48fd --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/5e/5e8aa769c1c11cd76dacec695ceb6102 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.01245 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546 new file mode 100644 index 00000000..f1ee69d5 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/64/64f56ead9607d441b3e6499517bba546 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.013068 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49 new file mode 100644 index 00000000..90d4a4c8 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/66/6638a2a2f711f34178b2f8ef7c51bd49 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010764 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b new file mode 100644 index 00000000..b3a8831d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6c/6cdac225b4d603050a5a6ffcb2ad523b @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010923 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373 new file mode 100644 index 00000000..66a14459 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4164cde7dc517b55021f31e652a373 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010678 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77 new file mode 100644 index 00000000..8fd2cd18 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6f4f2eb4fe96173ca9fdcc22c836cf77 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.017609 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7 new file mode 100644 index 00000000..b2bc8bd8 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/6f/6fb457ec7cb096bc6b8b7442d6eb7bf7 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.01784 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3 new file mode 100644 index 00000000..7074a206 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/79/790bf8c280b62b6da195591b8608e2c3 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.015092 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357 new file mode 100644 index 00000000..5e53c772 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7e/7e7fba10efe7cf51149ed3643b32f357 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.015117 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad new file mode 100644 index 00000000..cb32cf16 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/7f/7f8aa799ff46d92a4285df18b43026ad @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.006981 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a new file mode 100644 index 00000000..084db191 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/81/81e36cd7c1af621157d27c8704db1f8a @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.011357 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c new file mode 100644 index 00000000..1719c2db --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/82/8299d64c6cb862109461796e7b07a77c @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.010261 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045 new file mode 100644 index 00000000..045cfb65 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/86/8605df14510eb6c308c45fc361f17045 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.036923 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1 new file mode 100644 index 00000000..d648462a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8a/8a85d0bd2ca0c894340c27399caea9e1 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.006913 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9 new file mode 100644 index 00000000..13bd97d8 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8e7e6b7a37f4870cca825d4640e642a9 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.01024 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae new file mode 100644 index 00000000..6fdd0350 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8e/8ee6dc52a7f99925d560c9a2793409ae @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.017217 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5 new file mode 100644 index 00000000..11c71693 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/8f/8f6b84827b14b20ad4f55795ecc33da5 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.013809 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e new file mode 100644 index 00000000..2011973e --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/91/91e3acce87e899d31d72bfc4e344894e @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.009571 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081 new file mode 100644 index 00000000..ec03ed81 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/92/9285cd54491d9208c9b05d2ddc547081 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.018187 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813 new file mode 100644 index 00000000..eb8664f6 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/96/968a81aa5f33a7f54c9cefae9b292813 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.018046 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740 new file mode 100644 index 00000000..e6969806 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9a/9a773cdf73af5690d8a8420677f0f740 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.009723000000000001 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024 new file mode 100644 index 00000000..cdd15741 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/9e/9e8ce8946666fb9536598d9c2ae93024 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.011765 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb new file mode 100644 index 00000000..79182f8f --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a3/a3f21f0400e6d55abac5c856b28e9ffb @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.007213 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2 new file mode 100644 index 00000000..314ee002 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/a9/a99ce9aa870ce03416372c1a304e07d2 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.073545 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8 new file mode 100644 index 00000000..82a930b0 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ab/ab307d7398ae30cec85828c09471e2d8 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.023023 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a new file mode 100644 index 00000000..136d7383 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b3/b386b7fe2163827e25b780290f28a56a @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.008283 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff new file mode 100644 index 00000000..59b74aa3 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/b8/b8a150eafcc9e28b770d58a235778aff @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.011451 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b new file mode 100644 index 00000000..1d6d7f3d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bde5773e0993e0fc8cb2ae6f7275c32b @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.007758 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f new file mode 100644 index 00000000..663f9c65 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/bd/bdf0e9ec6a0784ede3eeca45d653790f @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.009259 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78 new file mode 100644 index 00000000..e154e34a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c4/c43334abf3abc246234be33ac096ce78 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.016119 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0 new file mode 100644 index 00000000..6857f2f4 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c618acb790425ac1d4cc72fcd127ace0 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.027442 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f new file mode 100644 index 00000000..02aef59a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c6/c6bd18162952013d3dfb676bc162459f @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.009154000000000001 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1 new file mode 100644 index 00000000..ade3fdd3 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/c9/c98d13b5b18abcc704601510cf32bdf1 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.016912 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7 new file mode 100644 index 00000000..77dab5b2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd3bc78fdd932f0acd847e8adebe01f7 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.009802999999999999 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88 new file mode 100644 index 00000000..51f0a7f0 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/cd/cd4bba0dc0badbfc7ea872449401cd88 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.008982 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546 new file mode 100644 index 00000000..3dd6ee70 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d1/d188627c778696fdf3bbe9bd44418546 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.378981 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26 new file mode 100644 index 00000000..b9299137 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/d7/d79b99d51417a0d365cca1f90f5d5d26 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.008257 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9 new file mode 100644 index 00000000..86ef7e57 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/df/dfd9b2245d87cbed501ff2b9d8e2dde9 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.020709 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374 new file mode 100644 index 00000000..b1f4520a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e3/e374265f63b80745a4379319733b7374 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.008892000000000001 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b new file mode 100644 index 00000000..6715abb2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e497228f4c2738e74aab252e3c78411b @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.028072 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8 new file mode 100644 index 00000000..8b8775b2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/e4/e4edd764a4b69ff6b22a8d14be0726d8 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.007492 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d new file mode 100644 index 00000000..32b88358 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ec/ecf9237df56d890381fb7505f5cb0e0d @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.013478 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4 new file mode 100644 index 00000000..636e0145 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ee/eee171d3f90d6c7bcff96dc68a118ff4 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.04214 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23 b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23 new file mode 100644 index 00000000..31640be9 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/alt-ergo@2.5.4/ff/ffc74e57bec68191ce00177e832d1b23 @@ -0,0 +1 @@ +{ "status": "Valid", "time": 0.050584 } diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version new file mode 100644 index 00000000..a81004d9 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/.why3find/version @@ -0,0 +1 @@ +v4@why3.1.8.0 diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile new file mode 100644 index 00000000..2efa3fa3 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/Makefile @@ -0,0 +1,26 @@ +.PHONY: all prove doc lib test clean + +all: prove doc lib + +prove: + why3find prove -l -x + +doc: + why3find doc -t ONNX *.mlw + +lib: + @mkdir -p lib/extract + @rm -fr lib/extract/* + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv sequence.Seq + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Shape + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Index + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv tensor.Tensor + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv opwhere.Where + why3 extract -D ocaml64 -o lib/extract --modular -L . -D tensor.drv opmatrix.Matrix + dune build + +test: + @dune runtest + +clean: + rm -fr lib/*.ml html diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md new file mode 100644 index 00000000..4d05db7a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/README.md @@ -0,0 +1,59 @@ +# ONNX Formal Methods + +To install the tool-chain: + + $ apt-get opam + $ opam install why3find alt-ergo + +To install the VSCode extension: + + $ wget https://git.frama-c.com/pub/why3find/-/jobs/1177337/artifacts/raw/vscode/why3-platform-1.1.1.vsix + $ code --install-extension why3-platform-1.1.1.vsix + +Documentation: + +- [Why3](https://www.why3.org) +- [Why3 Manual](https://www.why3.org/doc/) +- [Why3 Standard Library](https://www.why3.org/stdlib/) +- [Why3find](https://git.frama-c.com/pub/why3find) + +# Proving + + $ make prove + why3find prove -l -x + Theory opwhere.FlatWhere: ✔ (-) + Theory opwhere.Where: ✔ (2) + Theory utils.Product: ✔ (2) + Theory tensor.FlatTensor: ✔ (14) + Theory tensor.Tensor: ✔ (1) + Theory sequence.Seq: ✔ (24) + Theory sequence.Codomain: ✔ (6) + Cache 53/53 + Proofs ✔ (49) (unchanged) + Provers 691ms, depth: 3 + - alt-ergo@2.5.4 ( 49) ( 12ms - 44ms - 691ms) + - split_vc ( 8) + +# Documentation + + $ make doc + why3find doc *.mlw + Generated /Users/correnson/onnx/html/index.html + +# Extraction & Test + + $ make lib # extract and compile the lib + $ make test # compile and execute the tests + +The OCaml extracted code from Why3 is in the `lib` directory, which also contains +hand-written files: + + - `lib/dune` OCaml compilation directives + - `lib/tensor.mli` Public API of the Tensor library + - `lib/tensor.ml` Bindings of the public API to the extracted code + - `lib/extract/*.ml` Extracted code from Why3 specifications + +The unit tests are written in OCaml in `test` directory: + + - `test/dune` OCaml compilation directives + - `test/test.ml` OCaml unit tests using the Tensor library diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project new file mode 100644 index 00000000..e94f5dfb --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/dune-project @@ -0,0 +1,12 @@ +(lang dune 3.1) +(generate_opam_files true) +(using dune_site 0.1) +(name onnx) +(authors "Author Name(s)") +(maintainers "Maintainer Name(s)") +(license LICENSE) +(package + (name onnx) + (synopsis "short synopsis") + (description "longer description") + (depends why3find)) diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune new file mode 100644 index 00000000..1a535bd0 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/dune @@ -0,0 +1,5 @@ +(library + (name tensorlib) + (package onnx)) + +(include_subdirs unqualified) diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml new file mode 100644 index 00000000..2cf1cb77 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opmatrix__Matrix.ml @@ -0,0 +1,23 @@ +let swap (idx: (int) list) : (int) list = + match idx with + | i :: (j :: ([])) -> j :: i :: [] + | _ -> assert false (* absurd *) + +let get : type a. (a Tensor__Tensor.tensor) -> (int) -> (int) -> a = + fun t i j -> t.Tensor__Tensor.value (i :: j :: [] ) + +let cols : type a. (a Tensor__Tensor.tensor) -> (int) = + fun a -> Sequence__Seq.mixfix_lbrb a.Tensor__Tensor.shape 0 + +let rows : type a. (a Tensor__Tensor.tensor) -> (int) = + fun a -> Sequence__Seq.mixfix_lbrb a.Tensor__Tensor.shape 1 + +let transpose : + type a. (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) = + fun a -> let value (idx: (int) list) : a = + if Sequence__Seq.length idx = 2 + then a.Tensor__Tensor.value (swap idx) + else a.Tensor__Tensor.value idx in + { Tensor__Tensor.shape = swap a.Tensor__Tensor.shape; + Tensor__Tensor.value = value } + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml new file mode 100644 index 00000000..010a9ad0 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/opwhere__Where.ml @@ -0,0 +1,11 @@ +let where : + type a. ((bool) Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) -> + (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) = + fun cond a b -> { Tensor__Tensor.shape = + ((ignore ((ignore (b.Tensor__Tensor.shape) ; (a.Tensor__Tensor.shape))) ; + (cond.Tensor__Tensor.shape))); Tensor__Tensor.value = + (fun (i: (int) list) -> + if ((cond.Tensor__Tensor.value) i) + then ((a.Tensor__Tensor.value) i) + else ((b.Tensor__Tensor.value) i)) } + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml new file mode 100644 index 00000000..445d9e76 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/sequence__Seq.ml @@ -0,0 +1,77 @@ +type 'a seq = 'a list + +let length : type a. (a list) -> (int) = fun u -> ((List.length u)) + +let rec mixfix_lbrb : type a. (a list) -> (int) -> a = + fun u k -> match u with + | x :: w -> if k = 0 then x else mixfix_lbrb w (k - 1) + | _ -> assert false (* absurd *) + +let rec equal : + type a. (a -> (a -> (bool))) -> (a list) -> (a list) -> (bool) = + fun eq u v -> match (u, v) with + | (x :: u', y :: v') -> eq x y && equal eq u' v' + | ([], []) -> true + | _ -> false + +let empty : type a. (a list) = [] + +let elt : type a. a -> (a list) = fun e -> e :: [] + +let hd : type a. (a list) -> a = + fun s -> match s with + | x :: _ -> x + | _ -> assert false (* absurd *) + +let tl : type a. (a list) -> a = fun s -> mixfix_lbrb s (length s - 1) + +let rec create : type a. a -> (int) -> (a list) = + fun e n -> if n > 0 then e :: create e (n - 1) else [] + +let reset : type a b. a -> (b list) -> (a list) = + fun e s -> create e (length s) + +let rec init : type a. ((int) -> a) -> (int) -> (int) -> (a list) = + fun f a b -> if a < b then f a :: init f (a + 1) b else [] + +let rec map : type a b. (a -> b) -> (a list) -> (b list) = + fun f s -> match s with + | [] -> [] + | x :: xs -> f x :: map f xs + +let rec infix_plpl : type a. (a list) -> (a list) -> (a list) = + fun u v -> match u with + | [] -> v + | x :: w -> x :: infix_plpl w v + +let rec mixfix_lbdtdtrb : type a. (a list) -> (int) -> (int) -> (a list) = + fun u i j -> match u with + | [] -> [] + | x :: w -> + if 0 < i + then mixfix_lbdtdtrb w (i - 1) (j - 1) + else begin if 0 < j then x :: mixfix_lbdtdtrb w 0 (j - 1) else [] end + +let mixfix_lbdtdt_rb : type a. (a list) -> (int) -> (a list) = + fun u i -> mixfix_lbdtdtrb u 0 i + +let mixfix_lb_dtdtrb : type a. (a list) -> (int) -> (a list) = + fun u i -> mixfix_lbdtdtrb u i (length u) + +let infix_pldt : type a. (a list) -> a -> (a list) = + fun s e -> infix_plpl s (elt e) + +let infix_dtpl : type a. a -> (a list) -> (a list) = + fun e s -> infix_plpl (elt e) s + +let rec mixfix_lblsmnrb : type a. (a list) -> (int) -> a -> (a list) = + fun u i x -> match u with + | x0 :: w -> if i = 0 then x :: w else x0 :: mixfix_lblsmnrb w (i - 1) x + | _ -> assert false (* absurd *) + +let memcpy : + type a. (a list) -> (int) -> (a list) -> (int) -> (int) -> (a list) = + fun u i v j n -> infix_plpl (infix_plpl (mixfix_lbdtdt_rb u i) + (mixfix_lbdtdtrb v j (j + n))) + (mixfix_lb_dtdtrb u (i + n)) + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml new file mode 100644 index 00000000..067334be --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Index.ml @@ -0,0 +1,10 @@ +type index = (int) list + +let valid (idx: (int) list) (s: Tensor__Shape.shape) : bool = + let rec inrange (ks: (int) list) (ds: (int) list) : bool = + match (ks, ds) with + | ([], []) -> true + | (k :: ksr, d :: dsr) -> (0 <= k && k < d) && inrange ksr dsr + | _ -> false in + inrange idx s + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml new file mode 100644 index 00000000..70202444 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Shape.ml @@ -0,0 +1,9 @@ +type shape = (int) list + +let rec product (ds: (int) list) : int = + match ds with + | [] -> 1 + | d :: ds1 -> d * product ds1 + +let sizeof (s: shape) : int = product s + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml new file mode 100644 index 00000000..7cbe5e8d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/extract/tensor__Tensor.ml @@ -0,0 +1,18 @@ +type 'a tensor = { + shape: Tensor__Shape.shape; + value: (int) list -> 'a; + } + +let dim : type a. (a tensor) -> (int) = + fun t -> Sequence__Seq.length t.shape + +exception Invalid_index + +let mem : type a. ((int) list) -> (a tensor) -> (bool) = + fun idx t -> Tensor__Index.valid idx t.shape + +let get : type a. ((int) list) -> (a tensor) -> a = + fun idx t -> if Tensor__Index.valid idx t.shape + then t.value idx + else raise Invalid_index + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml new file mode 100644 index 00000000..90db725c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.ml @@ -0,0 +1,75 @@ +(* ONNX Tensor API *) + +module S = Tensor__Shape +module I = Tensor__Index +module T = Tensor__Tensor + +type 'a tensor = 'a T.tensor +let dim = T.dim +let shape t = t.T.shape + +let scalar v = + T.{ + shape = [] ; + value = fun _ -> v ; + } + +exception Invalid_index = T.Invalid_index +let mem = T.mem +let get = T.get + +let (.%[]) t k = get [k] t +let (.%[;..]) t ks = get (Array.to_list ks) t + +let vector vs = + let d = Array.of_list vs in + let n = Array.length d in + if n = 0 then invalid_arg "Tensor.vector" ; + T.{ + shape = [ n ] ; + value = (function [ k ] -> d.(k) | _ -> raise Invalid_index) ; + } + +let matrix vs = + let d = Array.map Array.of_list @@ Array.of_list vs in + let n = Array.length d in + if n = 0 then invalid_arg "Tensor.matrix" ; + let m = Array.length d.(0) in + if m = 0 then invalid_arg "Tensor.matrix" ; + if Array.exists (fun r -> Array.length r <> m) d then + invalid_arg "Tensor.matrix" ; + T.{ + shape = [ n ; m ] ; + value = (function [ i ; j ] -> d.(i).(j) | _ -> raise Invalid_index) ; + } + +let pretty pp fmt (t : 'a tensor) = + match t.shape with + | [] -> pp fmt @@ t.value [] + | [n] -> + begin + Format.fprintf fmt "[" ; + for i = 0 to n-1 do + Format.fprintf fmt " %a" pp @@ t.value [i] + done ; + Format.fprintf fmt " ]" ; + end + | [n;m] -> + begin + Format.fprintf fmt "@[" ; + for i = 0 to n-1 do + Format.fprintf fmt "[" ; + for j = 0 to m-1 do + Format.fprintf fmt " %a" pp @@ t.value [i;j] + done ; + Format.fprintf fmt " ]@," ; + done ; + Format.fprintf fmt "@]" ; + end + | _ -> invalid_arg "Tensor.pretty" + +let transpose (t : 'a tensor) = + if dim t <> 2 then invalid_arg "Tensor.transpose" ; + Opmatrix__Matrix.transpose t + +let where = Opwhere__Where.where diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli new file mode 100644 index 00000000..6c924c1c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/lib/tensor.mli @@ -0,0 +1,22 @@ +(** ONNX Tensor OCaml Interface *) + +type 'a tensor + +val dim : 'a tensor -> int +val shape : 'a tensor -> int list + +val scalar : 'a -> 'a tensor +val vector : 'a list -> 'a tensor +val matrix : 'a list list -> 'a tensor + +exception Invalid_index + +val mem : int list -> 'a tensor -> bool +val get : int list -> 'a tensor -> 'a +val (.%[]) : 'a tensor -> int -> 'a +val (.%[;..]) : 'a tensor -> int array -> 'a + +val pretty : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a tensor -> unit + +val transpose : 'a tensor -> 'a tensor +val where : bool tensor -> 'a tensor -> 'a tensor -> 'a tensor diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam new file mode 100644 index 00000000..1dba3db6 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/onnx.opam @@ -0,0 +1,28 @@ +# This file is generated by dune, edit dune-project instead +opam-version: "2.0" +synopsis: "short synopsis" +description: "longer description" +maintainer: ["Maintainer Name(s)"] +authors: ["Author Name(s)"] +license: "LICENSE" +depends: [ + "dune" {>= "3.1"} + "why3find" + "odoc" {with-doc} +] +build: [ + ["dune" "subst"] {dev} + [ + "dune" + "build" + "-p" + name + "-j" + jobs + "--promote-install-files=false" + "@install" + "@runtest" {with-test} + "@doc" {with-doc} + ] + ["dune" "install" "-p" name "--create-install-files" name] +] diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw new file mode 100644 index 00000000..f8a0b31a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix.mlw @@ -0,0 +1,56 @@ +(** + Specification of Matrix operations on tensors. + *) + +module Matrix + + use int.Int + use list.List + use sequence.Seq + use tensor.Tensor + let function swap (idx : seq int) : seq int + (*proof*) + requires { length idx = 2 } + ensures { length result = 2 } + ensures { Seq.(result[0] = idx[1]) } + ensures { Seq.(result[1] = idx[0]) } + = match idx with + | Cons i (Cons j Nil) -> Cons j (Cons i Nil) + end + (*qed*) + + let function get (t : tensor 'a) (i j : int) + (*proof*) + = t.value (Cons i (Cons j Nil)) + (*qed*) + + let function cols (a : tensor 'a) : int + requires { dim a = 2 } + ensures { result = Seq.( a.shape[0] ) } + = Seq.(a.shape.dims[0]) + let function rows (a : tensor 'a) : int + requires { dim a = 2 } + ensures { result = Seq.( a.shape[1] ) } + = Seq.(a.shape.dims[1]) + let transpose (a : tensor 'a) : tensor 'a + requires { dim a = 2 } + ensures { result.shape = swap a.shape } + ensures { + forall i j. + 0 <= i < cols result -> + 0 <= j < rows result -> + get result i j = get a j i + } + = let value (idx : index) : 'a = + if length idx = 2 then a.value (swap idx) else a.value idx + in + assert { + forall i j. + swap (Cons i (Cons j Nil)) == (Cons j (Cons i Nil)) + } ; + { + shape = { dims = swap a.shape.dims } ; + value = value + } + +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json new file mode 100644 index 00000000..dffdffca --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opmatrix/proof.json @@ -0,0 +1,21 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 17, "time": 0.607 } ], + "proofs": { + "Matrix": { + "cols": { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + "rows": { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + "swap": { "prover": "alt-ergo@2.5.4", "time": 0.118 }, + "transpose": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.066 }, + { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.036 } + ] + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw new file mode 100644 index 00000000..8c51725c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere.mlw @@ -0,0 +1,18 @@ +(** + Specification of Where operation on tensors. + *) + +module Where + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + + let function where (cond : tensor bool) (a b : tensor 'a) : tensor 'a = + { + shape = same cond.shape (same a.shape b.shape) ; + value = fun i -> if cond.value[i] then a.value[i] else b.value[i] ; + } + +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json new file mode 100644 index 00000000..192bfce4 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/opwhere/proof.json @@ -0,0 +1,7 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ], + "proofs": { + "FlatWhere": {}, + "Where": { "where": { "prover": "alt-ergo@2.5.4", "time": 0.024 } } + } +} diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml new file mode 100644 index 00000000..d57c5bfd --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/out/opwhere__Where.ml @@ -0,0 +1,10 @@ +let where : + type a. ((bool) Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) -> + (a Tensor__Tensor.tensor) -> (a Tensor__Tensor.tensor) = + fun cond a b -> { Tensor__Tensor.shape = cond.Tensor__Tensor.shape; + Tensor__Tensor.value = + (fun (i: (Z.t) list) -> + if Map__Map.mixfix_lbrb cond.Tensor__Tensor.value i + then Map__Map.mixfix_lbrb a.Tensor__Tensor.value i + else Map__Map.mixfix_lbrb b.Tensor__Tensor.value i) } + diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw new file mode 100644 index 00000000..d7fc3bae --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence.mlw @@ -0,0 +1,274 @@ +(*@ # Finite Sequences + + This modules provides a persistent array interface to lists. + It is typically used to model byte arrays of COSMO implementation. +*) + +module Seq + + use int.Int + use int.MinMax + use option.Option + use list.ListRich as L + + (*@ ## Definition + + A sequence `s` is defined by its size `n = length s` and its elements + `s[i]` with `0 <= i < n`. + + All other operations on sequences are defined + in terms of the length and elements of the returned sequences. + *) + + type seq 'a = L.list 'a + + (*@ Length of a sequence. *) + let function length (u : seq 'a) = L.length u + meta coercion function length + + (*@ Elements of a sequence. *) + let rec function ([]) (u : seq 'a) (k : int) : 'a + requires { 0 <= k < u } + ensures { L.nth k u = Some result } + (*proof*) + variant { u } + = match u with L.Cons x w -> if k = 0 then x else w[k-1] end + (*qed*) + + lemma head: forall x:'a, u. (L.Cons x u)[0] = x + lemma tail: forall x:'a, u k. 0 < k <= length u -> (L.Cons x u)[k] = u[k-1] + + (*@ Definitionnal equality: `u == v` states that sequences `u` and `v` have + the same length and same elements. *) + predicate (==) (u v : seq 'a) = + length u = length v /\ forall k. 0 <= k < u -> u[k] = v[k] + + (*@ Equal lists are definitionnaly equals. *) + lemma reflexivity : forall u : seq 'a. u == u + + (*@ Definitionnaly equal lists _are_ equals. *) + let rec lemma extensivity (a b : seq 'a) + requires { a == b } + ensures { a = b } + (*proof*) + variant { a, b } + = match a, b with + | L.Cons x a' , L.Cons y b' -> + assert { x = y } ; + let lemma shift (k: int) + requires { 0 <= k < length a' } + ensures { a'[k] = b'[k] } + = assert { a'[k] = a[k+1] } ; + assert { b'[k] = b[k+1] } + in extensivity a' b' + | _ -> () + end + (*qed*) + + (*@ Functional equality. *) + let rec equal (eq : 'a -> 'a -> bool) (u v : seq 'a) : bool + requires { forall x y. L.mem x u -> L.mem y v -> (eq x y <-> x = y) } + ensures { result <-> u = v } + (*proof*) + variant { u, v } + = match u, v with + | L.Cons x u', L.Cons y v' -> eq x y && equal eq u' v' + | L.Nil, L.Nil -> true + | _ -> false + end + (*qed*) + + (*@ ## Constructors + + Helper functions for constructing sequences. + *) + + (*@ Empty sequence of length `0`. *) + let constant empty : seq 'a = L.Nil + + (*@ Singleton sequence with a unique element. *) + let function elt (e : 'a) : seq 'a = L.Cons e L.Nil + + (*@ Head element *) + let function hd (s : seq 'a) : 'a + requires { 0 < s.length } + ensures { result = s[0] } + (*proof*) = match s with L.Cons x _ -> x end (*qed*) + + (*@ Tail element *) + let function tl (s : seq 'a) : 'a + requires { 0 < s.length } + ensures { result = s[ s.length - 1 ] } + (*proof*) = s[ s.length - 1 ] (*qed*) + + (*@ `create e n` returns a sequence of length `n` filled with `e` elements. *) + let rec function create (e : 'a) (n : int) : seq 'a + requires { 0 <= n } + ensures { length result = n } + ensures { forall k. 0 <= k < result -> result[k] = e } + (*proof*) + variant { n } + = if n > 0 then L.Cons e (create e (n-1)) else L.Nil + (*qed*) + + (*@ `reset e s` returns a sequence of same length `s` filled with `e` elements. *) + let function reset (e : 'a) (s : seq 'b) : seq 'a = create e s.length + + (*@ `init f a b` returns the sequence of elements `f i` for `a <= i < b`. *) + let rec function init (f : int -> 'a) (a b : int) : seq 'a + requires { a <= b } + ensures { length result = b-a } + ensures { forall k. 0 <= k < result -> result[k] = f (a+k) } + (*proof*) + variant { b-a } + = if a < b then L.Cons (f a) (init f (a+1) b) else L.Nil + (*qed*) + + (*@ `map f s` applies `f` to all elements f `s` *) + let rec function map (f : 'a -> 'b) (s : seq 'a) : seq 'b + ensures { length result = length s } + ensures { forall k. 0 <= k < result -> result[k] = f s[k] } + (*proof*) + variant { s } + = match s with L.Nil -> L.Nil | L.Cons x xs -> L.Cons (f x) (map f xs) end + (*qed*) + + (*@ ## Slices + + Sequences are commonly operated by a slice of their elements. + By convention, a slice `i..j` of elements denotes the range of elements + from `i` (_included_) to `j` (_excluded_), hence the range of indices `k` + such that `i <= k < j`. + + The slice of all elements of a sequence of size `n` is then `0..n`, and + we also denote slice `0..i` by `..i` and slice `i..n` by `i..`. + Slice operations are properly defined only for a valid range of indices. + + The dual operation of slice is the concatenation, here denoted by `(++)`. + *) + + (*@ `u ++ v` is the concatenation of `u` and `v`, + where `u` elements comes first, and `v` elements follows. *) + let rec function (++) (u v : seq 'a) : seq 'a + ensures { length result = length u + length v } + ensures { forall k. 0 <= k < u -> result[k] = u[k] } + ensures { forall k. u <= k < result -> result[k] = v[k - u] } + (*proof*) + variant { u } + = match u with L.Nil -> v | L.Cons x w -> L.Cons x (w ++ v) end + (*qed*) + + (*@ `u[i..j]` is the slice of `u` elements + from index `i` (_included_) to index `j` (_excluded_). *) + let rec function ([..]) (u : seq 'a) (i j : int) : seq 'a + requires { 0 <= i <= j <= u } + ensures { length result = j-i } + ensures { forall k. 0 <= k < result -> result[k] = u[i+k] } + (*proof*) + variant { u } + = match u with + | L.Nil -> L.Nil + | L.Cons x w -> + if 0 < i then w[i-1..j-1] else + if 0 < j then L.Cons x w[0..j-1] + else L.Nil + end + (*qed*) + + (*@ `u[..i]` is the slice of `u` elements up to index `i` (_excluded_). *) + let function ([.._]) (u : seq 'a) (i : int) : seq 'a + requires { 0 <= i <= u } + ensures { length result = i } + ensures { forall k. 0 <= k < result -> result[k] = u[k] } + = u[0..i] + + (*@ `u[i..]` is the slice of `u` elements from index `i` (_included_). *) + let function ([_..]) (u : seq 'a) (i : int) : seq 'a + requires { 0 <= i <= u } + ensures { result = u - i } + ensures { forall k. 0 <= k < result -> result[k] = u[i+k] } + = u[i..length u] + + (*@ This property illustrates the duality of slice & concat. *) + goal split: forall u : seq 'a, i. + 0 <= i <= u -> + u[..i] ++ u[i..] = u + (*proof*) + by u[..i] ++ u[i..] == u + (*qed*) + + (*@ This property illustrates the duality of slice & concat. *) + goal fullsplit: forall u : seq 'a, i j. + 0 <= i <= j <= u -> + u[..i] ++ u[i..j] ++ u[j..] = u + (*proof*) + by u[..i] ++ u[i..j] ++ u[j..] == u + (*qed*) + + (*@ Tail insertion *) + let function (+.) (s : seq 'a) (e : 'a) : seq 'a + ensures { length result = length s + 1 } + ensures { forall k. 0 <= k < s -> result[k] = s[k] } + ensures { result[ s ] = e } + (*proof*) = s ++ elt e (*qed*) + + (*@ Head insertion *) + let function (.+) (e : 'a) (s : seq 'a) : seq 'a + ensures { length result = length s + 1 } + ensures { forall k. 0 <= k < s -> result[1+k] = s[k] } + ensures { result[ 0 ] = e } + (*proof*) = elt e ++ s (*qed*) + + (*@ ## Updates & Copies *) + + (*@ `u[i<-x]` returns a copy of the sequence `u` where the element `u[i]` + has been replaced by `x`. *) + let rec function ([<-]) (u : seq 'a) (i : int) (x : 'a) : seq 'a + requires { 0 <= i < u } + ensures { length result = length u } + ensures { forall k. 0 <= k < result -> result[k] = if k=i then x else u[k] } + (*proof*) + variant { u } + = match u with L.Cons x0 w -> + if i = 0 then L.Cons x w else L.Cons x0 w[ i-1 <- x ] + end + (*qed*) + + (*@ `memcpy u i v j n` returns a copy of `u` where the slice `u[i..i+n]` has been + replaced with the slice `v[j..j+n]` (last excluded). *) + let function memcpy (u : seq 'a) (i : int) (v : seq 'a) (j : int) (n : int) + requires { 0 <= i <= i+n <= u } + requires { 0 <= j <= j+n <= v } + ensures { length result = length u } + ensures { forall k. 0 <= k < i -> result[k] = u[k] } + ensures { forall k. i <= k < i+n -> result[k] = v[j+k-i] } + ensures { forall k. i+n <= k < u -> result[k] = u[k] } + = u[..i] ++ v[j..j+n] ++ u[i+n..] + + (*@ `havoc u v p n` asserts that sequences `u` and `v` have identical elements + but elements in range `p..p+n` (last excluded). *) + predicate havoc (u v: seq 'a) (p n : int) = + u.length = v.length /\ + (forall k. 0 <= k < p -> u[k] = v[k]) /\ + (forall k. p+n <= k < u -> u[k] = v[k]) + +end + +(*@ ## Sequence Codomain *) + +module Codomain + + use int.Int + use set.Set + use Seq + + let rec ghost function codomain (s : seq 'a) : set 'a + ensures { forall v. Set.mem v result <-> exists i. 0 <= i < s /\ s[i] = v } + (*proof*) + variant { s.length } + = if s.length = 0 then Set.empty else + let k = s.length-1 in + Set.add s[k] (codomain s[0..k]) + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json new file mode 100644 index 00000000..26c7e1a1 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/sequence/proof.json @@ -0,0 +1,64 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ], + "proofs": { + "Codomain": { + "codomain": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.086 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.691 } + ] + } + ] + } + ] + } + }, + "Seq": { + "create": { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + "equal": { "prover": "alt-ergo@2.5.4", "time": 0.086 }, + "extensivity": { "prover": "alt-ergo@2.5.4", "time": 0.115 }, + "fullsplit": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + }, + "hd": { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + "head": { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + "infix ++": { "prover": "alt-ergo@2.5.4", "time": 0.061 }, + "infix +.": { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + "infix .+": { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + "init": { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + "map": { "prover": "alt-ergo@2.5.4", "time": 0.043 }, + "memcpy": { "prover": "alt-ergo@2.5.4", "time": 0.078 }, + "mixfix [..]": { "prover": "alt-ergo@2.5.4", "time": 0.068 }, + "mixfix [.._]": { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + "mixfix [<-]": { "prover": "alt-ergo@2.5.4", "time": 0.052 }, + "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + "mixfix [_..]": { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + "reflexivity": { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + "reset": { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + "split": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 } + ] + }, + "tail": { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + "tl": { "prover": "alt-ergo@2.5.4", "time": 0.024 } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv new file mode 100644 index 00000000..36294f0f --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.drv @@ -0,0 +1,37 @@ +(* ONNX driver extensions for OCaml extraction *) + +module int.Int + syntax type int "int" + syntax literal int "%1" + syntax val zero "0" + syntax val one "1" + syntax val ( + ) "%1 + %2" prec 8 8 7 + syntax val ( - ) "%1 - %2" prec 8 8 7 + syntax val (-_) "- %1" prec 5 4 + syntax val ( * ) "%1 * %2" prec 7 7 6 + syntax val (=) "%1 = %2" prec 11 11 10 + syntax val (<=) "%1 <= %2" prec 11 11 10 + syntax val (<) "%1 < %2" prec 11 11 10 + syntax val (>=) "%1 >= %2" prec 11 11 10 + syntax val (>) "%1 > %2" prec 11 11 10 +end + +module list.Length + syntax val length "(List.length %1)" +end + +module map.Map + syntax type map "%1 -> %2" + syntax val get "%1 %2" + syntax val ([]) "%1 %2" + syntax val set "(fun _x -> if _x = %2 then %3 else %1 _x)" + syntax val ([<-]) "(fun _x -> if _x = %2 then %3 else %1 _x)" +end + +module map.Const + syntax val const "(fun _ -> %1)" +end + +module utils.Same + syntax val same "(ignore %2 ; %1)" +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw new file mode 100644 index 00000000..845333b4 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor.mlw @@ -0,0 +1,124 @@ +(** + ONNX Tensor Definition. + *) + +module Shape + use int.Int + use list.Mem + use list.List + use sequence.Seq + + predicate positive (ds : seq int) = + forall i. 0 <= i < length ds -> 0 < ds[i] + + type shape = { dims : seq int } + invariant { positive dims } + meta coercion function dims + + (*proof*) + (* Helper function for defining product *) + let rec function product (ds : list int) : int = + variant { ds } + match ds with + | Nil -> 1 + | Cons d ds -> d * product ds + end + + (* Lemma: shifted sequence *) + let ghost shift (xs : list 'a) (x : 'a) (rxs : list 'a) + requires { xs = Cons x rxs } + ensures { x = xs[0] } + ensures { forall i. 0 <= i < length rxs -> rxs[i] = xs[i+1] } + = () + + (* Lemma: forall s, 1 <= product s *) + let rec lemma product1 (ds : list int) + requires { positive ds } + ensures { 1 <= product ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons d rds -> + ghost shift ds d rds ; + product1 rds + end + (*qed*) + (*qed*) + + let sizeof (s : shape) : int = product s.dims + +end + +module Index + use int.Int + use bool.Bool + use list.Mem + use list.List + use sequence.Seq + use Shape + + type index = seq int + + predicate valid (idx : index) (s : shape) = + length idx = length s /\ + forall i. 0 <= i < length s -> 0 <= idx[i] < s[i] + + let valid (idx : index) (s : shape) : bool + ensures { result <-> valid idx s } + (*proof*) + (* Efficient Implementation *) + = let rec inrange (ks ds : list int) : bool + requires { positive ds } + ensures { result <-> + ( length ks = length ds /\ + forall i. 0 <= i < length ks -> 0 <= ks[i] < ds[i] ) + } + variant { ds } + = match ks, ds with + | Nil, Nil -> true + | Cons k ksr, Cons d dsr -> + ghost shift ks k ksr ; + ghost shift ds d dsr ; + 0 <= k < d && inrange ksr dsr + | _ -> false + end + in inrange idx s.dims + (*qed*) + +end + +module Tensor + use int.Int + use sequence.Seq + use export Shape + use export Index + + type tensor 'a = { + shape : shape ; + value : index -> 'a ; + } + + let function dim (t : tensor 'a) : int = length t.shape.dims + + + predicate (<<) (idx : index) (t : tensor 'a) = valid idx t.shape + function ([]) (t : tensor 'a) (idx : index) : 'a = t.value idx + + exception Invalid_index + + let mem (idx : index) (t : tensor 'a) : bool + ensures { result <-> idx << t } + (*proof*) + = valid idx t.shape + (*qed*) + + let get (idx : index) (t : tensor 'a) : 'a + ensures { idx << t } + ensures { result = t[idx] } + raises { Invalid_index -> not (idx << t) } + (*proof*) + = if valid idx t.shape then t.value idx else raise Invalid_index + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json new file mode 100644 index 00000000..85363c13 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/tensor/proof.json @@ -0,0 +1,116 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ], + "proofs": { + "FlatTensor": { + "mixfix [<-]": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 } + ] + }, + "mixfix []": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 } + ] + }, + "mixfix []<-": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 } + ] + }, + "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + "shape": { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.013 } + }, + "Index": { + "valid": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + { "prover": "alt-ergo@2.5.4", "time": 0.044 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.032 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.046 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 } + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 } + ] + } + ] + } + }, + "Shape": { + "product": { "prover": "alt-ergo@2.5.4", "time": 0.026 }, + "product1": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + { "prover": "alt-ergo@2.5.4", "time": 0.051 } + ] + }, + "shape": { "prover": "alt-ergo@2.5.4", "time": 0.031 }, + "shift": { "prover": "alt-ergo@2.5.4", "time": 0.022 } + }, + "Tensor": { + "get": { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + "mem": { "prover": "alt-ergo@2.5.4", "time": 0.022 } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune new file mode 100644 index 00000000..f413be10 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/dune @@ -0,0 +1,4 @@ +(tests + (names matrix where) + (flags ("-open" "Tensorlib")) + (libraries tensorlib)) diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected new file mode 100644 index 00000000..49915a28 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.expected @@ -0,0 +1,2 @@ + A = [ 1 2 3 ][ 4 5 6 ] +tA = [ 1 4 ][ 2 5 ][ 3 6 ] diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml new file mode 100644 index 00000000..faa31664 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/matrix.ml @@ -0,0 +1,10 @@ +let pp_int fmt = Format.fprintf fmt "%2d" +let pp_matrix = Tensor.pretty pp_int + +let a = Tensor.matrix [ + [ 1; 2; 3 ]; + [ 4; 5; 6 ]; +] + +let () = Format.printf " A = %a@." pp_matrix a +let () = Format.printf "tA = %a@." pp_matrix (Tensor.transpose a) diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml new file mode 100644 index 00000000..653e3139 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/test.ml @@ -0,0 +1,23 @@ + +(* pretty printer for float scalars *) +let pp_real fmt v = Format.fprintf fmt "%3.2f" v +(* pretty printer for boolean scalars *) +let pp_bool fmt v = Format.fprintf fmt "%c" (if v then 'T' else 'F') + +let v = Tensor.scalar 0.0 + +let () = Format.printf "Scalar: %a@." (Tensor.pretty pp_real) v + +let c = Tensor.vector [true;true;false;true;false] +let a = Tensor.vector [1.0;2.0;3.0;4.0;5.0] +let b = Tensor.vector [0.1;0.2;0.3;0.4;0.5] + +let w = Tensor.where c a b + +let () = + begin + Format.printf "C: %a@." (Tensor.pretty pp_bool) c ; + Format.printf "A: %a@." (Tensor.pretty pp_real) a ; + Format.printf "B: %a@." (Tensor.pretty pp_real) b ; + Format.printf "W: %a@." (Tensor.pretty pp_real) w ; + end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected new file mode 100644 index 00000000..91df10af --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.expected @@ -0,0 +1,4 @@ +C: [ T T F T F ] +A: [ 1.00 2.00 3.00 4.00 5.00 ] +B: [ 0.10 0.20 0.30 0.40 0.50 ] +W: [ 1.00 2.00 0.30 4.00 0.50 ] diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml new file mode 100644 index 00000000..a96f7170 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/test/where.ml @@ -0,0 +1,15 @@ +let pp_real fmt v = Format.fprintf fmt "%3.2f" v +let pp_bool fmt v = Format.fprintf fmt "%c" (if v then 'T' else 'F') + +let c = Tensor.vector [true;true;false;true;false] +let a = Tensor.vector [1.0;2.0;3.0;4.0;5.0] +let b = Tensor.vector [0.1;0.2;0.3;0.4;0.5] +let w = Tensor.where c a b + +let () = + begin + Format.printf "C: %a@." (Tensor.pretty pp_bool) c ; + Format.printf "A: %a@." (Tensor.pretty pp_real) a ; + Format.printf "B: %a@." (Tensor.pretty pp_real) b ; + Format.printf "W: %a@." (Tensor.pretty pp_real) w ; + end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw new file mode 100644 index 00000000..9ebff261 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils.mlw @@ -0,0 +1,25 @@ +module Same + + val function same (a b : 'a) : 'a + ensures { a = b -> result = a = b } + +end + +module Product + use int.Int + + (** + Notation for product, for `a <= i < b` of `f i` + *) + let rec function product (a b : int) (f : int -> int) : int + (*proof*) + variant { b-a } + = if b <= a then 1 else f a * product (a+1) b f + (*qed*) + + lemma empty: forall a b f. b <= a -> product a b f = 1 + + axiom equal: forall a b f g. + (forall i. a <= i < b -> f i = g i) -> product a b f = product a b g + +end diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json new file mode 100644 index 00000000..3eccaef9 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/utils/proof.json @@ -0,0 +1,10 @@ +{ + "profile": [ { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.207 } ], + "proofs": { + "Product": { + "empty": { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + "product": { "prover": "alt-ergo@2.5.4", "time": 0.012 } + }, + "Same": {} + } +} diff --git a/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json new file mode 100644 index 00000000..09ea7fdc --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/code/2025-03-19/onnx/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo" ], + "tactics": [ "split_vc" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio new file mode 100644 index 00000000..c62cfdaf --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.drawio @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png new file mode 100644 index 00000000..b32c392d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/imgs/2025-03-19/formal_spec_ver_phases.png differ diff --git a/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png b/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png new file mode 100644 index 00000000..fa574af0 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/imgs/2025-06-20/image.png differ diff --git a/safety-related-profile/meetings/formal_methods/imgs/README.md b/safety-related-profile/meetings/formal_methods/imgs/README.md new file mode 100644 index 00000000..43effb8a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/imgs/README.md @@ -0,0 +1 @@ +All pictures referenced in the meeting minutes. \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md b/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md new file mode 100644 index 00000000..9a593e7d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/2026-02-18 - Inputs.md @@ -0,0 +1,143 @@ +*Please provide your comments, questions, proposals here. They will be discussed during the Feb 18th 2026 workshop.* + +### Code generation +When we try to generate code for the `Where` operator, the C file is empty... +For more details: If we separate the general specification (ctensor) from the operator specification (abstract + concrete specification), generating C code produces the ctensor implementation (ctensor.c), but not the operator implementation (where.c). + +### Formalization of floating point operators +- What do really want to express / need? +- How shall we specify operations in F (floating point numbers)? + - For simple operations such as Add() or Mul(), we could specify the operation wrt their counter parts in R, e.g. something like: if $x$ and $y$ are FP tensors, and $+$ and $+.$ are the addition operators for floats and real, respectively: + $$ + round(x+y)=round(round(x) + round(y)) + $$ + - Shall we adopt this "strategy" for simple operations (extending it to inf and Nan, non representable numbers)? even if it can't be appied to other operations... +- Ideally : the formalization of floating point number and their operations should include the effect of rounding and the special values +- Nota: In Why3, there are axiomatic models of IEEE754 numbers (see [Formalization of Floating-Point Arithmetic](https://www.why3.org/stdlib/ieee_float.html)). + The module provides constructors, some operations (add, sub, etc.). + It also provides some axioms that define the expected behaviour of FP numbers, by relating the result to the operation in R e.g., + ``` + axiom add_finite: forall m:mode, x y:t [add m x y]. + is_finite x -> is_finite y -> no_overflow m (to_real x +. to_real y) -> + is_finite (add m x y) /\ + to_real (add m x y) = round m (to_real x +. to_real y) + ``` +- Bottom line: model floating point operations as operations in R extended with Infs and NaN. + +### Modeling of booleans +- Currely, booelans are modeled using floating point numbers (the value is "True" if it is greater than 0). +- WOuld it be possible / make sense to use "built-in" booleans (in the "high-level" spec) and integers (in the "low-level" spec), with a mapping between them (`function b_to_i(nb: bool) : int = if b then 1 else 0`). + +--- + +### Two levels of formalization +- (João and Ricardo) + - Is there any motivation/reason for having two levels of formalization (abstract and concrete)? + + - We assume that one of the reasons behind this choice was to have a link between the naturally structural representation of tensors, used by ONNX and multiple other frameworks, and the target representation proposed by SONNX (a flattened array). Is there any other motivation for adopting such formalisation strategy? + + - As far as we have explored, in the abstract spec, the formalization seems to be carried out using a more functional style; on the other hand, in the concrete formalization a more imperative style is adopted.´ + + - Is this what is intended? + + - If so, do the proofs become more complicated when different styles are used? +--- +### Empty tensors +- (João and Ricardo) + - We believe that, at this moment, in the abstract formalization, it is not possible to represent **empty tensors** - tensors with at least one of their dimensions equal to **zero**. What leads us to believe this is the following invariant. + + ``` + invariant { positive dims } + ``` + - Is there any reason why empty tensors are not allowed in the abstract formalization? + + \ + We have noticed that, in the concrete formalization, several functions make use of the predicate `valid_tensor`, present in the file `libtensor.mlw`, or of the predicate `dimension`, present in the file `libvector.mlw`. + + ``` + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + ``` + ``` + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + ``` + The predicate `dimension` in turn uses the predicate `pdim`, which is defined as: + + ``` + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + ``` + + - Despite the absence of direct invariants over the ctensor type and the fact that we can create empty tensors at this level of the formalisations, they would not hold the predicate `valid_tensor`. After discussing this in the WG it became quite clear that empty tensors are indeed to be allowed. If so, what would be the consequences of this both at the abstract and concrete level? + +**Suggestion** +- From our point of view these are the changes that would need to be done at each one of the levels: + + - **Abstract level**: + - Instead of a **positive** predicate we would have to define a **non-negative** + + - All the **let rec lemmas** previously defined for **positive** invariant would need to be adjusted to support the **non-negative** invariant + + - **Concrete level**: + - Creation of an **ndim** predicate that is equal to **pdim** but instead of saying **positive**, says **non-negative** + + - Creation of an **ndimension** predicate which copies the semantics of **dimension**, calling **ndim** instead of **pdim** + - Adjust all **let rec lemmas** that already exist for predicate **pdim** to be in conformity with the predicate **ndim** + - **cdim_size** should now accept a list of dimensions with 0s and therefore the **let ghost construct** _**mult_bound**_ does not hold in this context. There seems to be two possible ways of solving such issue: + - Change **let ghost construct** _**mult_bound**_ definition to hold for lists with non negative values (including zero). We have already tried this one, however, we were not successful. + + - Having a **null_dimensions_check** that checks wether any dimension in the specified list is 0. **cdim_size** would now have an if statement based on the result of **null_dimensions_check**. + - **Valid_tensor** predicate will then be changed to use **ndimension** predicate instead of **dimension**. + +- [Empty tensors suggestion](./empty_tensors) +--- +### Function Contracts +- (João and Ricardo) + + - In the abstract world, the definition and specification of functions enter as logical context as they are all ghost functions. It seems to us important to have contracts as they are the assurance that based on some well-defined pre-conditions some post-conditions will be verified for all possible inputs. Based on that we are struggling to understand what are the role of these contracts in the abstract level. + + - Should they exist and specify properties of the functions? + + - Should they not be present at this level? + + - Should they be part only at specific functions in this level? + + - Furthermore, and related to this, we would like to have your input in one of the structural operators we have worked on - flatten. The flatten's informal and formal specifications are, respectively, in the following links: [flatten informal specification](../../../sonnx/ops/spec/informal/flatten/flatten.md), [flatten formalization](../../../sonnx/ops/spec/formal/flatten/flatten.mlw). + + - What is your opinion with regard to the abstract level contracts? (Are they enough? Should they be discarded? This question is not specific to the flatten function, but also to every auxiliar function in the file.) + + - For this operator the concrete level formalisation will only make a copy of two different arrays. + + - In such case how strong and meaningful should our invariant be in order to prove the link (post-condition/assert) between both levels? + + - Do have any suggestions so that we can successfully prove the relation between both levels for this operator? + + - For some operators wich we have already worked on, the Cformalisation implementation uses the **malloc** function. In such cases, for acessing the data we have to check if the pointer is not null. To do so we are currently using an if-statement. An operator in wich our formalisation strategy uses **malloc** is the [matmul formalization](../../../sonnx/ops/spec/formal/matmul/matmul.mlw). Regarding this strategy we fall in the same problem stated above: how can we sucessfully prove the relation between both levels for this operator, particularly in the case where **malloc** returns a null pointer. + +--- +### Generic tensors and type-specific tensors +- (João and Ricardo) + - The strategy of formalisation used so far is not generic with respect to the **datatype of the ctensors**. In order to turn this strategy type-generic we have explored two different alternatives: + + - **Clone-based approach** + + - In this strategy we **declare** three types: a **concrete datatype**, an **abstract datatype** and a **function** that maps a concrete datatype to the respective abstract datatype. + + - The already existing functions in the **ctensor module** were adapted to use the appropriate types and the respective mapping function. + + - To create a specific tensor datatype we need only to clone the module instantiating the **concrete datatype**, the **abstract datatype** and the **function** that maps a concrete datatype to the respective abstract datatype. + + - We were able to **generate C-code**, and **prove the cWhere operator** while using such strategy. + + - **CTensor parametrized datatype** + + - Similar to what was used for the abstract tensors formalisation, using an **a'** and a **b'** datatype to capture the concrete, abstract levels, respectively. Every function that needs to map the concrete onto the abstract would need to receive as an **argument** the **mapping function** that goes from **a'** to **b'**. + + - With this strategy we were not able to extract C-code. + + - What is your opinion regarding these two approaches? Is one of them more suited than the other, or is there any other approach that would be more adequate? + +- [Generic tensors suggestion](./generic_tensors) diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile new file mode 100644 index 00000000..132ecf50 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/Makefile @@ -0,0 +1,37 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib cc clean + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +update: + why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" + +EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr lib + @why3 extract $(EXTRACT) libvector.CIndex + @why3 extract $(EXTRACT) libtensor.CTensor + @cd lib && gcc -c *.c + @find lib -type f | sort + +clean: + rm -fr lib html \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv new file mode 100644 index 00000000..7115821a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/cdriver.drv @@ -0,0 +1,38 @@ +module std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module std.Cfloat + interface export "#include " + + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module libvector.CIndex + interface "#include " + interface "#include " +end + +module libtensor.CTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw new file mode 100644 index 00000000..fd7bcf7b --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use std.Int + use std.List + use tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json new file mode 100644 index 00000000..e220781a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/proof.json @@ -0,0 +1,12 @@ +{ + "profile": [], + "proofs": { + "CFlat": { + "index_of_offset": null, + "index_range": null, + "offset_of_index": null, + "offset_push": null, + "offset_size": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml new file mode 100644 index 00000000..34174adf --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak new file mode 100644 index 00000000..34174adf --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3session.xml.bak @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz new file mode 100644 index 00000000..16d9c3fe Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak new file mode 100644 index 00000000..16d9c3fe Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/layout/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c new file mode 100644 index 00000000..e92baf0a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.c @@ -0,0 +1,80 @@ +#include "cindex.h" + +int null_dimension_check(int32_t * u, int32_t n) { + int is_null_dim; + int32_t i, o; + is_null_dim = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + if (u[i] == 0) { + is_null_dim = 1; + } + if (i == o) { + break; + } + } + } + return is_null_dim; +} + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + if (null_dimension_check(u, n)) { + return 0; + } else { + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; + } +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h new file mode 100644 index 00000000..b689813d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.h @@ -0,0 +1,17 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int null_dimension_check(int32_t * u, int32_t n); + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o new file mode 100644 index 00000000..209fc57d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/cindex.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c new file mode 100644 index 00000000..8c7c9520 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.c @@ -0,0 +1,58 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0) < cond.t_data[i] ? a.t_data[i] : b.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h new file mode 100644 index 00000000..a9da013a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.h @@ -0,0 +1,24 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o new file mode 100644 index 00000000..7406bb9c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/lib/ctensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw new file mode 100644 index 00000000..31700330 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor.mlw @@ -0,0 +1,153 @@ +module CTensor + use std.Int + use std.List + use std.Clib + use std.Cfloat + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + + (** C Tensor **) + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + (** Dimension List **) + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + (** Number of entries **) + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + (** Valid index predicate **) + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + (** Empty tensor predicate **) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + (** Valid tensor **) + + (*New*) + predicate valid_tensor (t : ctensor) = + ndimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + (*End New*) + + (*Coordinate Index for ctensor*) + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else 0.0 + + function tensor_value (t : ctensor) : list int -> real = + fun k -> tensor_value_at k t + + function tensor_valueb (t : ctensor) : list int -> bool = + fun k -> Real.(tensor_value_at k t > 0.0) + + let ghost function tensor (t : ctensor) : tensor real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = 0.0 ; + } + + let ghost function tensorb (t : ctensor) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + + (*New*) + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { ndimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + (*End New*) + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_where (cond a b r : ctensor) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + (*qed*) + requires { tensorb cond ~ tensor a ~ tensor b } + ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) } + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json new file mode 100644 index 00000000..e6f16a32 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/proof.json @@ -0,0 +1,13 @@ +{ + "profile": [], + "proofs": { + "CTensor": { + "ctensor_clear": null, + "ctensor_create": null, + "ctensor_reset": null, + "ctensor_where": null, + "tensor": null, + "tensorb": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml new file mode 100644 index 00000000..cde8ef5b --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak new file mode 100644 index 00000000..cde8ef5b --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3session.xml.bak @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz new file mode 100644 index 00000000..278c545d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak new file mode 100644 index 00000000..278c545d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libtensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw new file mode 100644 index 00000000..34b6c547 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector.mlw @@ -0,0 +1,187 @@ +(** C-Library to compute Offsets & Dimensions *) + +module CIndex + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use layout.CFlat + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + (*New*) + predicate ndim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 <= value_at u k + (*End New*) + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + + (*New*) + predicate ndimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ ndim u 0 n /\ 0 < vdim u n <= max_int32 + (*End New*) + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + (*New*) + (** Equivalence betwen {ndim} and {tensor.Range.non_negative}. *) + let rec lemma non_negative_ndim (u : iarray) (p q : int) + (*proof*) + ensures { ndim u p q <-> non_negative (islice u p q) } + variant { q - p } + = if p < q then non_negative_ndim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { ndim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + + let rec lemma vdim_zero (u:iarray) (n:int) + requires { ndim u 0 n } + ensures { (exists i. 0 <= i < n /\ value_at u i = 0) <-> vdim u n = 0 } + variant { n } + = if n > 0 then + if value_at u (n-1) = 0 then + () + else + vdim_zero u (n-1) + + let null_dimension_check (u : iarray) (n : int32) : bool = + requires { ndimension u n } + ensures { result = (exists i. 0 <= i < n /\ value_at u i = 0) } + ensures { result -> vdim u n = 0 } + ensures { not result = (forall i. 0 <= i < n -> value_at u i <> 0) } + ensures { not result -> vdim u n > 0 } + (*proof*) + let ref is_null_dim = false in + for i = 0 to n - 1 do + invariant { + (is_null_dim = false -> forall k. 0 <= k < i -> value_at u k <> 0) /\ + (is_null_dim = true -> exists j. 0 <= j < i /\ value_at u j = 0) + } + if u[i] = 0 then + is_null_dim <- true + done ; + is_null_dim + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { ndimension u n } + (*qed*) + if null_dimension_check u n then + 0 + else + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> ndimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> ndimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + (*End New*) + + + (** ## Coordinates *) + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { dimension ds n } + ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + let ref p = 0 in + for i = 0 to n - 1 do + (*proof*) + invariant { valid (ivector ks i) (ivector ds i) } + invariant { p = offset (ivector ks i) (ivector ds i) } + (*qed*) + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + begin + (*proof*) + assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ; + ghost + begin + ensures { Int32.in_bounds (p * d) } + ensures { Int32.in_bounds (p * d + k) } + let ghost dl = pure { sdim ds 0 i } in + let ghost di = Int32.to_int ds[i] in + let ghost dr = pure { sdim ds (i+1) n } in + let ghost dn = pure { sdim ds 0 n } in + sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * d + k ; + end + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json new file mode 100644 index 00000000..94eecafd --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/proof.json @@ -0,0 +1,13 @@ +{ + "profile": [], + "proofs": { + "CIndex": { + "cdim_create_1": null, + "cdim_create_2": null, + "cdim_size": null, + "coffset": null, + "positive_pdim": null, + "sdim_split": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml new file mode 100644 index 00000000..25c72ed2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak new file mode 100644 index 00000000..25c72ed2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3session.xml.bak @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz new file mode 100644 index 00000000..b15d3416 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak new file mode 100644 index 00000000..b15d3416 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/libvector/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw new file mode 100644 index 00000000..244ee393 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std.mlw @@ -0,0 +1,234 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + use list.List + use list.Length + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () + + (** Helper function to extract element from list at given position *) + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + type uint32 = UInt32.uint32 + + val function to_uint32 (v : int32) : uint32 + requires { 0 <= v } + ensures { Int32.to_int v = UInt32.to_int result } + + (** Null-Pointer *) + val predicate is_null (vp : ptr 'a) : bool + ensures { result <-> vp.zone = null_zone } + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset + p /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + +val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + +end + + +module Cint + use int.Int + use mach.int.Int32 + use mach.int.Int32 as Int32 + use mach.int.UInt32 + use mach.int.UInt32 as UInt32 + use int.ComputerDivision + use Clib + + val function ( .+ ) (a b : int) : int + ensures { result = a + b } + + val function ( .- ) (a b : int) : int + ensures { result = a - b } + + val function ( .* ) (a b : int) : int + ensures { result = a * b } + + val function ( ./ ) (a b : int) : int + requires { b <> 0 } + ensures { result = ComputerDivision.div (a) ( b) } + + val function of_int (v : int) : Int32.int32 + ensures { Int32.to_int result = v } + + val function malloc_int (s: uint32) : ptr int + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + val function get (p: ptr int) (k: Int32.int32) : int + ensures { result = p[k] } + + val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + let ([]) (p : ptr int) (k :Int32.int32) : int = get p k + +end + +module Cfloat + use int.Int + use real.Real + use real.FromInt + + type float = abstract { to_real : real } + meta coercion function to_real + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + + val function f32 (k : f32) : float + ensures { result = f32'real k } + + val function f64 (k : f64) : float + ensures { result = f64'real k } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { Real.( result = a + b ) } + + val function (.-) (a b : float) : float + ensures { Real.( result = a - b ) } + + val function (.-_) (a : float) : float + ensures { Real.( result = (- a) ) } + + val function ( .* ) (a b : float) : float + ensures { Real.( result = a * b ) } + + val function ( ./ ) (a b : float) : float + requires { b <> 0.0 } + ensures { Real.( result = a / b ) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> Real.( a = b ) } + + val predicate ( .< ) (a b : float) : bool + ensures { result <-> Real.( a < b ) } + + val predicate ( .<= ) (a b : float) : bool + ensures { result <-> Real.( a <= b ) } + +end \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json new file mode 100644 index 00000000..636ea4c6 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/proof.json @@ -0,0 +1,20 @@ +{ + "profile": [], + "proofs": { + "Cfloat": { + "one": { "tactic": "split_vc", "children": [] }, + "zero": { "tactic": "split_vc", "children": [] } + }, + "Cint": { "mixfix []": { "tactic": "split_vc", "children": [] } }, + "Clib": { + "mixfix []": null, + "mixfix []<-": null, + "slice": null, + "slice_append": null, + "valid_in_range": null, + "vector_push": null + }, + "Int": { "euclide": null, "get_dim": null, "mult_bound": null }, + "List": { "map_concat": null } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml new file mode 100644 index 00000000..75bc70f5 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak new file mode 100644 index 00000000..75bc70f5 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3session.xml.bak @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz new file mode 100644 index 00000000..38e5086b Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak new file mode 100644 index 00000000..38e5086b Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/std/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw new file mode 100644 index 00000000..9b6263c4 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor.mlw @@ -0,0 +1,196 @@ +(** Formalization of coordinates and dimensions *) + +module Range + use int.Int + use std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + (* New *) + predicate non_negative (ds: list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 <= d /\ non_negative ds + end + (* End New *) + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma valid_push (ks ds : list int) (k d : int) + requires { 0 <= k < d } + requires { valid ks ds } + ensures { valid (push ks k) (push ds d) } + (*proof*) + variant { ks } + = match ks , ds with + | Nil , Nil -> () + | Cons _ ks , Cons _ ds -> valid_push ks ds k d + | _ -> absurd + end + (*qed*) + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + + (* New *) + let rec lemma non_negative_size (ds : list int) + requires { non_negative ds } + ensures { 0 <= size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> non_negative_size ds end + (*qed*) + + let rec lemma non_negative_valid (ks ds : list int) + requires { valid ks ds } + ensures { non_negative ds } + ensures { 0 <= size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> non_negative_valid ks ds + | _ -> () + end + (*qed*) + (* End New *) + +end + + +(** Formalization of Tensor *) +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + background : 'a ; (* default value, or value for 0-dimensions tensor *) + } + + (*New*) + invariant { non_negative dims } + (*End New*) + + invariant { forall k. valid k dims \/ data k = background } + (*proof*) + by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d } + (*qed*) + + meta coercion function dims + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + + (** Tensor with the same dimensions and background value *) + predicate (~=) (a : tensor 'a) (b : tensor 'a) = + a ~ b /\ a.background = b.background + + (** Equal tensors *) + predicate (==) (a b : tensor 'a) = + a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k + + (** Extensionality *) + lemma exteq: forall a b : tensor 'a. a == b <-> a = b + (*proof*) by tensor'eq a b (*qed*) + + (** Scalar Tensor *) + let ghost function scalar (v : 'a) : tensor 'a + ensures { result.dims = Nil } + ensures { result.background = v } + ensures { forall k. result k = v } + (*proof*) + = { dims = Nil ; data = (fun _ -> v) ; background = v } + (*qed*) + + (*New*) + (** Null Tensor *) + let ghost function zero (e : 'a) (ds : list int) : tensor 'a + requires { non_negative ds } + ensures { result.dims = ds } + ensures { result.background = e } + ensures { forall k. result k = e } + (*proof*) + = { dims = ds ; data = (fun _ -> e) ; background = e } + (*qed*) + + + (** Constant Tensor *) + let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a + ensures { result.dims = ds } + requires { non_negative ds } + ensures { result.background = bg } + ensures { forall k. valid k ds -> result k = v } + (*proof*) + = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg } + (*qed*) + + (** Constant & Null *) + goal zero_is_const: forall e : 'a, ds. non_negative ds -> zero e ds == const e e ds + (*End New*) +end + +(** OP-Where Tensor Operation *) +module OPWhere + use Tensor + + let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) + +end \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json new file mode 100644 index 00000000..73b56dab --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/proof.json @@ -0,0 +1,24 @@ +{ + "profile": [], + "proofs": { + "OPWhere": { + "dwhere": { "tactic": "split_vc", "children": [] }, + "opwhere": null + }, + "Range": { + "positive_size": null, + "positive_valid": null, + "size_append": null, + "size_push": null, + "valid_push": null + }, + "Tensor": { + "const": null, + "exteq": null, + "scalar": null, + "tensor": null, + "zero": null, + "zero_is_const": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml new file mode 100644 index 00000000..29b7ec0e --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak new file mode 100644 index 00000000..29b7ec0e --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3session.xml.bak @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz new file mode 100644 index 00000000..7610217c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak new file mode 100644 index 00000000..7610217c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/empty_tensors/tensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile new file mode 100644 index 00000000..b9f86156 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/Makefile @@ -0,0 +1,41 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib cc clean + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +update: + why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" + +EXTRACT=-D c -D cdriver.drv -L . --modular --interface -o lib + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr lib + @why3 extract $(EXTRACT) libvector.CIndex + @why3 extract $(EXTRACT) libtensor.CTensor + @why3 extract $(EXTRACT) specifictensor.CInt32Tensor + @why3 extract $(EXTRACT) specifictensor.CInt64Tensor + @why3 extract $(EXTRACT) specifictensor.CBooleanTensor + @why3 extract $(EXTRACT) specifictensor.CFloatTensor + @cd lib && gcc -c *.c + @find lib -type f | sort + +clean: + rm -fr lib html diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv new file mode 100644 index 00000000..3a8870d9 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/cdriver.drv @@ -0,0 +1,68 @@ +module std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module std.Cfloat + interface export "#include " + + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module libvector.CIndex + interface "#include " + interface "#include " +end + +module libtensor.CTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end + +module specifictensor.CInt32Tensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end + + +module specifictensor.CInt64Tensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end + +module specifictensor.CBooleanTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end + + +module specifictensor.CFloatTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include " +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw new file mode 100644 index 00000000..c5052c7d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor.mlw @@ -0,0 +1,117 @@ +module GenericTensor + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + + (* data_type defines the type of t_data on the ctensor *) + type data_type + (* abstract_data_type defines the type of abstract data corresponding to data_type *) + type abstract_data_type + (* Function to convert data_type to abstract_data_type *) + (* Here the function is only declared *) + function to_abstract (v : data_type) : abstract_data_type + + (* Note *) + (* When clonning this module, data_type, abstract_data_type and to_abstract must be defined *) + + + (** C Tensor **) + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : ptr data_type ; + } + + (** Dimension List **) + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + (** Number of entries **) + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + (** Valid index predicate **) + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + (** Empty tensor predicate **) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + (** Valid tensor **) + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + (*Coordinate Index for ctensor*) + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) (cbackground : data_type) : abstract_data_type = + if valid_index k t then + to_abstract (value_at t.t_data (tensor_offset k t)) + else to_abstract cbackground + + function tensor_value (t : ctensor) (cbackground : data_type) : list int -> abstract_data_type = + fun k -> tensor_value_at k t cbackground + + let ghost function tensor (t : ctensor) (cbackground : data_type) : tensor abstract_data_type + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t cbackground } + ensures { result.background = to_abstract cbackground } + (*qed*) + = + { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t cbackground } ; + background = to_abstract cbackground ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + + let ctensor_clear (r : ctensor) (cbackground : data_type) = + requires { valid_tensor r } + ensures { tensor r cbackground = Tensor.zero (to_abstract cbackground) (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> to_abstract (value_at r.t_data k) = to_abstract cbackground } + (*qed*) + r.t_data[i] <- cbackground + done + (*proof*) + ; assert { tensor r cbackground == Tensor.zero (to_abstract cbackground) (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : data_type) (cbackground : data_type) = + requires { valid_tensor r } + ensures { tensor r cbackground == Tensor.const (to_abstract v) (to_abstract cbackground) (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r cbackground == Tensor.const (to_abstract v) (to_abstract cbackground) (tensor_dim r) } + (*qed*) + + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml new file mode 100644 index 00000000..f5b85d44 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak new file mode 100644 index 00000000..f5b85d44 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3session.xml.bak @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz new file mode 100644 index 00000000..53882cc8 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak new file mode 100644 index 00000000..53882cc8 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/generictensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw new file mode 100644 index 00000000..fd7bcf7b --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use std.Int + use std.List + use tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json new file mode 100644 index 00000000..e220781a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/proof.json @@ -0,0 +1,12 @@ +{ + "profile": [], + "proofs": { + "CFlat": { + "index_of_offset": null, + "index_range": null, + "offset_of_index": null, + "offset_push": null, + "offset_size": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml new file mode 100644 index 00000000..4683ff19 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak new file mode 100644 index 00000000..4683ff19 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3session.xml.bak @@ -0,0 +1,241 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz new file mode 100644 index 00000000..46e3585c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak new file mode 100644 index 00000000..46e3585c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/layout/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c new file mode 100644 index 00000000..b587a3cb --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.c @@ -0,0 +1,43 @@ +#include "cbooleantensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + int * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(int)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r, int cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = cbackground; + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, int v, int cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h new file mode 100644 index 00000000..bfe1f9f0 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.h @@ -0,0 +1,21 @@ +#ifndef CBOOLEANTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + int * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r, int cbackground); + +void ctensor_reset(struct ctensor r, int v, int cbackground); + +#define CBOOLEANTENSOR_H_INCLUDED +#endif // CBOOLEANTENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o new file mode 100644 index 00000000..c4887610 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cbooleantensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c new file mode 100644 index 00000000..38d7e795 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.c @@ -0,0 +1,43 @@ +#include "cfloattensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r, double cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = cbackground; + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v, double cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h new file mode 100644 index 00000000..be29a6cb --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.h @@ -0,0 +1,21 @@ +#ifndef CFLOATTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r, double cbackground); + +void ctensor_reset(struct ctensor r, double v, double cbackground); + +#define CFLOATTENSOR_H_INCLUDED +#endif // CFLOATTENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o new file mode 100644 index 00000000..4c45b456 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cfloattensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o new file mode 100644 index 00000000..90634a76 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cindex.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c new file mode 100644 index 00000000..35430cbf --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.c @@ -0,0 +1,43 @@ +#include "cint32tensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + int32_t * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(int32_t)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r, int32_t cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = cbackground; + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, int32_t v, int32_t cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h new file mode 100644 index 00000000..ad8b53b5 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.h @@ -0,0 +1,21 @@ +#ifndef CINT32TENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + int32_t * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r, int32_t cbackground); + +void ctensor_reset(struct ctensor r, int32_t v, int32_t cbackground); + +#define CINT32TENSOR_H_INCLUDED +#endif // CINT32TENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o new file mode 100644 index 00000000..1661c736 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint32tensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c new file mode 100644 index 00000000..97df9d41 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.c @@ -0,0 +1,43 @@ +#include "cint64tensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + int64_t * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(int64_t)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r, int64_t cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = cbackground; + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, int64_t v, int64_t cbackground) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h new file mode 100644 index 00000000..90eff13c --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.h @@ -0,0 +1,21 @@ +#ifndef CINT64TENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + int64_t * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r, int64_t cbackground); + +void ctensor_reset(struct ctensor r, int64_t v, int64_t cbackground); + +#define CINT64TENSOR_H_INCLUDED +#endif // CINT64TENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o new file mode 100644 index 00000000..43cce2ac Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/cint64tensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c new file mode 100644 index 00000000..8c7c9520 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.c @@ -0,0 +1,58 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0) < cond.t_data[i] ? a.t_data[i] : b.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h new file mode 100644 index 00000000..a9da013a --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.h @@ -0,0 +1,24 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" +#include + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o new file mode 100644 index 00000000..7406bb9c Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/lib/ctensor.o differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw new file mode 100644 index 00000000..a8322fac --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor.mlw @@ -0,0 +1,151 @@ +module CTensor + use std.Int + use std.List + use std.Clib + use std.Cfloat + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + + (** C Tensor **) + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + (** Dimension List **) + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + (** Number of entries **) + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + (** Valid index predicate **) + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + (** Empty tensor predicate **) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + (** Valid tensor **) + (**valid dimensions**) + (**valid_range [0, Number of entries] are valid indices**) + (**data writable??**) + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + (*Coordinate Index for ctensor*) + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else 0.0 + + function tensor_value (t : ctensor) : list int -> real = + fun k -> tensor_value_at k t + + function tensor_valueb (t : ctensor) : list int -> bool = + fun k -> Real.(tensor_value_at k t > 0.0) + + let ghost function tensor (t : ctensor) : tensor real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = 0.0 ; + } + + let ghost function tensorb (t : ctensor) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_where (cond a b r : ctensor) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + (*qed*) + requires { tensorb cond ~ tensor a ~ tensor b } + ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) } + (*qed*) + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json new file mode 100644 index 00000000..e6f16a32 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/proof.json @@ -0,0 +1,13 @@ +{ + "profile": [], + "proofs": { + "CTensor": { + "ctensor_clear": null, + "ctensor_create": null, + "ctensor_reset": null, + "ctensor_where": null, + "tensor": null, + "tensorb": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml new file mode 100644 index 00000000..7bb3ecf2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak new file mode 100644 index 00000000..7bb3ecf2 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3session.xml.bak @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz new file mode 100644 index 00000000..89f716d3 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak new file mode 100644 index 00000000..89f716d3 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libtensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw new file mode 100644 index 00000000..19e3903b --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector.mlw @@ -0,0 +1,135 @@ +(** C-Library to compute Offsets & Dimensions *) + +module CIndex + use std.Int + use std.List + use std.Clib + use mach.int.Int32 + use tensor.Range + use layout.CFlat + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { pdim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + (** Extracted Library *) + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { dimension u n } + (*qed*) + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> dimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> dimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + + (** ## Coordinates *) + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { dimension ds n } + ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + let ref p = 0 in + for i = 0 to n - 1 do + (*proof*) + invariant { valid (ivector ks i) (ivector ds i) } + invariant { p = offset (ivector ks i) (ivector ds i) } + (*qed*) + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + begin + (*proof*) + assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ; + ghost + begin + ensures { Int32.in_bounds (p * d) } + ensures { Int32.in_bounds (p * d + k) } + let ghost dl = pure { sdim ds 0 i } in + let ghost di = Int32.to_int ds[i] in + let ghost dr = pure { sdim ds (i+1) n } in + let ghost dn = pure { sdim ds 0 n } in + sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * d + k ; + end + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json new file mode 100644 index 00000000..94eecafd --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/proof.json @@ -0,0 +1,13 @@ +{ + "profile": [], + "proofs": { + "CIndex": { + "cdim_create_1": null, + "cdim_create_2": null, + "cdim_size": null, + "coffset": null, + "positive_pdim": null, + "sdim_split": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml new file mode 100644 index 00000000..ac5cc189 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak new file mode 100644 index 00000000..ac5cc189 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3session.xml.bak @@ -0,0 +1,461 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz new file mode 100644 index 00000000..b7e55530 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak new file mode 100644 index 00000000..b7e55530 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/libvector/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw new file mode 100644 index 00000000..08052637 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor.mlw @@ -0,0 +1,224 @@ +module CInt32Tensor + + use mach.int.Int32 + use int.Int + use list.List + use tensor.Tensor + + constant zero : int32 = 0 + clone export generictensor.GenericTensor with + type data_type = int32, + type abstract_data_type = int, + function to_abstract = Int32.to_int + + type ctensorint32 = ctensor + +end + +module CInt64Tensor + + use mach.int.Int64 + use int.Int + use list.List + use tensor.Tensor + + constant zero : int64 = 0 + clone export generictensor.GenericTensor with + type data_type = int64, + type abstract_data_type = int, + function to_abstract = Int64.to_int + + type ctensorint64 = ctensor + +end + +module CBooleanTensor + + use int.Int + use list.List + use tensor.Tensor + + function id (b : bool) : bool = b + + constant zero : bool = false + clone export generictensor.GenericTensor with + type data_type = bool, + type abstract_data_type = bool, + function to_abstract = id + + type ctensorbool = ctensor + +end + + + +module CFloatTensor + + use std.Cfloat + use real.Real + use std.Clib + use list.List + use tensor.Tensor + + constant zero : float = Cfloat.zero + clone export generictensor.GenericTensor with + type data_type = float, + type abstract_data_type = real, + function to_abstract = Cfloat.to_real + + type ctensorfloat = ctensor + + (*Remove this?*) + function tensor_valueb (t : ctensorfloat) : list int -> bool = + fun k -> Real.(tensor_value_at k t Cfloat.zero > 0.0) + + let ghost function tensorb (t : ctensorfloat) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + +end + + +module CWhere1 + + use CFloatTensor + use CBooleanTensor + use libvector.CIndex + use mach.int.Int32 + use std.Cfloat + use std.Clib + use int.Int + use tensor.OPWhere + use tensor.Tensor + + + let ctensor_where (cond: ctensorbool) (a b r : ctensorfloat) = + (*proof*) + requires { CBooleanTensor.valid_tensor cond } + requires { CFloatTensor.valid_tensor a } + requires { CFloatTensor.valid_tensor b } + requires { CFloatTensor.valid_tensor r } + requires { CFloatTensor.tensor a Cfloat.zero ~= CFloatTensor.tensor b Cfloat.zero ~= CFloatTensor.tensor r Cfloat.zero } + (*qed*) + requires { CBooleanTensor.tensor cond CBooleanTensor.zero ~ CFloatTensor.tensor a Cfloat.zero ~ CFloatTensor.tensor b Cfloat.zero } + ensures { CFloatTensor.tensor r Cfloat.zero = opwhere (CBooleanTensor.tensor cond CBooleanTensor.zero) + (CFloatTensor.tensor a Cfloat.zero) (CFloatTensor.tensor b Cfloat.zero) } + let m = cdim_size r.CFloatTensor.t_dims r.CFloatTensor.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.CFloatTensor.t_data k = + if value_at cond.CBooleanTensor.t_data k + then a.CFloatTensor.t_data[k] else b.CFloatTensor.t_data[k] + } + (*qed*) + r.CFloatTensor.t_data[i] <- + if cond.CBooleanTensor.t_data[i] then a.CFloatTensor.t_data[i] else b.CFloatTensor.t_data[i] + done + (*proof*) + ; assert { CFloatTensor.tensor r Cfloat.zero == opwhere (CBooleanTensor.tensor cond CBooleanTensor.zero) (CFloatTensor.tensor a Cfloat.zero) + (CFloatTensor.tensor b Cfloat.zero) } + (*qed*) + + +end + + +module CWhere + + use CFloatTensor + use libvector.CIndex + use mach.int.Int32 + use std.Cfloat + use std.Clib + use int.Int + use tensor.OPWhere + use tensor.Tensor + + + let ctensor_where (cond a b r : ctensorfloat) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a Cfloat.zero ~= tensor b Cfloat.zero ~= tensor r Cfloat.zero } + (*qed*) + requires { tensorb cond ~ tensor a Cfloat.zero ~ tensor b Cfloat.zero } + ensures { tensor r Cfloat.zero = opwhere (tensorb cond) (tensor a Cfloat.zero) (tensor b Cfloat.zero) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r Cfloat.zero == opwhere (tensorb cond) (tensor a Cfloat.zero) (tensor b Cfloat.zero) } + (*qed*) + + +end + + + + +module Test + + (* =============================== *) + (* Demonstration Module - Important *) + (* This module is not intended for proofs. *) + (* Its purpose is to illustrate best practices when working with multiple cloned datatypes from the generic tensor module. *) + (* In particular, it shows how to properly prefix variables and functions to avoid ambiguity + and ensure clarity when using two or more different tensor types in the same context. *) + (* =============================== *) + + use generictensor.GenericTensor + use CInt32Tensor + use CInt64Tensor + use tensor.Tensor + use libvector.CIndex + use mach.int.Int32 + use mach.int.Int64 + use std.Clib + use int.Int + + let ctensor_where (cond : ctensorint32) (a b r : ctensorint64) (b64 : int64) (b32 : int32) = + requires { CInt32Tensor.valid_tensor cond } + requires { CInt64Tensor.valid_tensor a } + requires { CInt64Tensor.valid_tensor b } + requires { CInt64Tensor.valid_tensor r } + requires { CInt64Tensor.tensor a b64 ~= CInt64Tensor.tensor b b64 ~= CInt64Tensor.tensor r b64 } + requires { CInt32Tensor.tensor cond b32 ~ CInt64Tensor.tensor a b64 ~ CInt64Tensor.tensor b b64 } + let m = cdim_size (CInt32Tensor.t_dims cond) (CInt32Tensor.t_rank cond) in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> + value_at (CInt64Tensor.t_data r) k = + if 0 < value_at (CInt32Tensor.t_data cond) k then + value_at (CInt64Tensor.t_data a) k + else + value_at (CInt64Tensor.t_data b) k + } + (*qed*) + (CInt64Tensor.t_data r)[i] <- if 0 < (CInt32Tensor.t_data cond)[i] then (CInt64Tensor.t_data a)[i] else (CInt64Tensor.t_data b)[i] + done; + + +end \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml new file mode 100644 index 00000000..c26c3066 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak new file mode 100644 index 00000000..c26c3066 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3session.xml.bak @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz new file mode 100644 index 00000000..f98b6f5d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak new file mode 100644 index 00000000..f98b6f5d Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/specifictensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw new file mode 100644 index 00000000..244ee393 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std.mlw @@ -0,0 +1,234 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + use list.List + use list.Length + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () + + (** Helper function to extract element from list at given position *) + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + type uint32 = UInt32.uint32 + + val function to_uint32 (v : int32) : uint32 + requires { 0 <= v } + ensures { Int32.to_int v = UInt32.to_int result } + + (** Null-Pointer *) + val predicate is_null (vp : ptr 'a) : bool + ensures { result <-> vp.zone = null_zone } + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset + p /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + +val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + +end + + +module Cint + use int.Int + use mach.int.Int32 + use mach.int.Int32 as Int32 + use mach.int.UInt32 + use mach.int.UInt32 as UInt32 + use int.ComputerDivision + use Clib + + val function ( .+ ) (a b : int) : int + ensures { result = a + b } + + val function ( .- ) (a b : int) : int + ensures { result = a - b } + + val function ( .* ) (a b : int) : int + ensures { result = a * b } + + val function ( ./ ) (a b : int) : int + requires { b <> 0 } + ensures { result = ComputerDivision.div (a) ( b) } + + val function of_int (v : int) : Int32.int32 + ensures { Int32.to_int result = v } + + val function malloc_int (s: uint32) : ptr int + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + val function get (p: ptr int) (k: Int32.int32) : int + ensures { result = p[k] } + + val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + let ([]) (p : ptr int) (k :Int32.int32) : int = get p k + +end + +module Cfloat + use int.Int + use real.Real + use real.FromInt + + type float = abstract { to_real : real } + meta coercion function to_real + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + + val function f32 (k : f32) : float + ensures { result = f32'real k } + + val function f64 (k : f64) : float + ensures { result = f64'real k } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { Real.( result = a + b ) } + + val function (.-) (a b : float) : float + ensures { Real.( result = a - b ) } + + val function (.-_) (a : float) : float + ensures { Real.( result = (- a) ) } + + val function ( .* ) (a b : float) : float + ensures { Real.( result = a * b ) } + + val function ( ./ ) (a b : float) : float + requires { b <> 0.0 } + ensures { Real.( result = a / b ) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> Real.( a = b ) } + + val predicate ( .< ) (a b : float) : bool + ensures { result <-> Real.( a < b ) } + + val predicate ( .<= ) (a b : float) : bool + ensures { result <-> Real.( a <= b ) } + +end \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json new file mode 100644 index 00000000..636ea4c6 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/proof.json @@ -0,0 +1,20 @@ +{ + "profile": [], + "proofs": { + "Cfloat": { + "one": { "tactic": "split_vc", "children": [] }, + "zero": { "tactic": "split_vc", "children": [] } + }, + "Cint": { "mixfix []": { "tactic": "split_vc", "children": [] } }, + "Clib": { + "mixfix []": null, + "mixfix []<-": null, + "slice": null, + "slice_append": null, + "valid_in_range": null, + "vector_push": null + }, + "Int": { "euclide": null, "get_dim": null, "mult_bound": null }, + "List": { "map_concat": null } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml new file mode 100644 index 00000000..438f2e62 --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak new file mode 100644 index 00000000..cba4785d --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3session.xml.bak @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz new file mode 100644 index 00000000..38e5086b Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak new file mode 100644 index 00000000..d99d9940 Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/std/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw new file mode 100644 index 00000000..7001ccdf --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor.mlw @@ -0,0 +1,778 @@ +(** Formalization of coordinates and dimensions *) + +module Range + use int.Int + use std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma valid_push (ks ds : list int) (k d : int) + requires { 0 <= k < d } + requires { valid ks ds } + ensures { valid (push ks k) (push ds d) } + (*proof*) + variant { ks } + = match ks , ds with + | Nil , Nil -> () + | Cons _ ks , Cons _ ds -> valid_push ks ds k d + | _ -> absurd + end + (*qed*) + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + +end + +(** Formalization of Tensor *) +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + background : 'a ; (* default value, or value for 0-dimensions tensor *) + } + + invariant { positive dims } + invariant { forall k. valid k dims \/ data k = background } + (*proof*) + by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d } + (*qed*) + + meta coercion function dims + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + + (** Tensor with the same dimensions and background value *) + predicate (~=) (a : tensor 'a) (b : tensor 'a) = + a ~ b /\ a.background = b.background + + (** Equal tensors *) + predicate (==) (a b : tensor 'a) = + a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k + + (** Extensionality *) + lemma exteq: forall a b : tensor 'a. a == b <-> a = b + (*proof*) by tensor'eq a b (*qed*) + + (** Scalar Tensor *) + let ghost function scalar (v : 'a) : tensor 'a + ensures { result.dims = Nil } + ensures { result.background = v } + ensures { forall k. result k = v } + (*proof*) + = { dims = Nil ; data = (fun _ -> v) ; background = v } + (*qed*) + + (** Null Tensor *) + let ghost function zero (e : 'a) (ds : list int) : tensor 'a + requires { positive ds } + ensures { result.dims = ds } + ensures { result.background = e } + ensures { forall k. result k = e } + (*proof*) + = { dims = ds ; data = (fun _ -> e) ; background = e } + (*qed*) + + + (** Constant Tensor *) + let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a + ensures { result.dims = ds } + requires { positive ds } + ensures { result.background = bg } + ensures { forall k. valid k ds -> result k = v } + (*proof*) + = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg } + (*qed*) + + (** Constant & Null *) + goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds + +end + +(** OP-Where Tensor Operation *) +module OPWhere + use Tensor + + let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) + +end + +(* +module OPSlice + use Tensor + use Range + use int.Int + use int.EuclideanDivision + use list.List + use list.Length + use list.Nth + use option.Option + use list.Map + use list.Mem + + (*Notas*) + (*Possivelmente remover os contratos porque a função é pura*) + (*As pre condições podem ser necessarias. Pos podem ser removidas à partida*) + (*MUITO IMPORTANTE: usamos um axioma para ajudar com a sobrejetividade mas isto pode ser problemático.*) + function get (o: option int): int = + match o with + | Some v -> v + | None -> -1 + end + + function my_nth (l: list int) (i: int): int = + match nth i l with + | Some d -> d + | None -> -1 + end + + + function head (l: list int): int = + match l with + | Cons d _ -> d + | Nil -> -1 + end + + + let ghost function normalize_axis (dims: list int) (axis_dim : list int) (axis: tensor int): tensor int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { length dims = size axis.dims } + requires { axis.dims = axis_dim } + (*A [C2]*) + requires { forall ks. valid ks axis_dim -> -length dims <= axis.data ks <= length dims - 1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim -> + (mod ((axis.data ks1) + length dims) (length dims)) = (mod ((axis.data ks2) + length dims) (length dims)) -> ks1 = ks2 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (*Normalization*) + ensures { forall ks. valid ks axis_dim -> 0 <= result.data ks < length dims } + ensures {forall ks. valid ks axis_dim -> axis.data ks < 0 -> result.data ks = axis.data ks + length dims } + ensures {forall ks. valid ks axis_dim -> axis.data ks >= 0 -> result.data ks = axis.data ks } + (*Index Uniqueness*) + ensures { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim -> result.data ks1 = result.data ks2 -> ks1 = ks2 } + (*Same dimension, Same background*) + ensures { result ~= axis} + (*proof*) + = let data = fun ks -> + if valid ks axis_dim then + let a = axis.data ks in + if a < 0 then a + length dims else a + else axis.background + in { dims = axis_dim ; data = data ; background = axis.background } + + let ghost function normalize_start (dims: list int) (start_dims: list int) (start axis : tensor int): tensor int + (*Additionally*) + requires { start_dims = start.dims } + (*1D Index Tensors*) + requires { length start.dims = length axis.dims = 1 } + (*X [C1]*) + requires { length dims = size start.dims = size axis.dims } + (*Redundant with 2 previous requires*) + requires { start ~ axis } + (*A [C2]*) + requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims -> + (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 } + (*S [C2]*) + requires { forall ks. valid ks start.dims -> -my_nth dims (axis.data ks) <= start.data ks <= my_nth dims (axis.data ks) - 1 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (*Normalization*) + ensures { forall ks. valid ks start.dims -> 0 <= result.data ks < my_nth dims (axis.data ks) } + ensures { forall ks. valid ks start.dims -> start.data ks < 0 -> result.data ks = start.data ks + my_nth dims (axis.data ks) } + ensures { forall ks. valid ks start.dims -> start.data ks >= 0 -> result.data ks = start.data ks } + (*Same dimension, Same background*) + ensures { result ~= start } + (*proof*) + = let data = fun ks -> + if valid ks start_dims then + let s = start.data ks in + let axis_index = axis.data ks in + if s < 0 then s + my_nth dims axis_index else s + else start.background + in { dims = start_dims ; data = data ; background = start.background } + + let ghost function normalize_end (dims: list int) (ends_dims: list int) (end_ axis steps: tensor int): tensor int + (*Additionally*) + requires { ends_dims = end_.dims } + (*1D Index Tensors*) + requires { length end_.dims = length axis.dims = length steps.dims = 1 } + (**X [C1]*) + requires { length dims = size end_.dims = size axis.dims = size steps.dims } + (*Redundant with previous requires*) + requires { end_ ~ axis ~ steps } + (*A [C2]*) + requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims -> + (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 } + (*K [C2]*) + requires { forall ks. valid ks steps.dims -> steps.data ks <> 0 } + (*E [C2]*) + requires { forall ks. valid ks end_.dims -> steps.data ks > 0 -> + let idx = my_nth dims (axis.data ks) in + -(idx) <= end_.data ks <= idx } + (*E [C3]*) + requires { forall ks. valid ks end_.dims -> steps.data ks < 0 -> + let idx = my_nth dims (axis.data ks) in + -(idx) - 1 <= end_.data ks <= idx - 1 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (**Normalization*) + ensures { forall ks. valid ks end_.dims -> + let idx = my_nth dims (axis.data ks) in + let step = steps.data ks in + let e = end_.data ks in + let res = if e < 0 then e + idx else e in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + ensures { forall ks. valid ks end_.dims -> + let idx = my_nth dims (axis.data ks) in + let e = end_.data ks in + result.data ks = (if e < 0 then e + idx else e) + } + (*Same dimension, Same background*) + ensures { result ~= end_ } + (*proof*) + = let data = fun ks -> + if valid ks ends_dims then + let e = end_.data ks in + let axis_index = axis.data ks in + if e < 0 then e + my_nth dims axis_index else e + else end_.background + in { dims = ends_dims ; data = data ; background = end_.background } + + + let rec ghost function dY_shape_rec (x:tensor 'a) (s e k a: tensor int) (t: list int) (i: int) : list int + (*1D Index Tensors*) + requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*Normalize End*) + requires { forall ks. valid ks e.dims -> + let idx = my_nth x.dims (a.data ks) in + let step = k.data ks in + let e_value = e.data ks in + let res = if e_value < 0 then e_value + idx else e_value in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + (*K [C2]*) + (*requires { forall ks. valid ks k.dims -> k.data ks <> 0 }*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + (*Real Index t*) + requires { forall h. 0 <= h < length x.dims -> mem h t } + (*R6*) + requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks } + (*R7*) + requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks } + (*Y [C2]*) + requires { + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 + } + requires { 0 <= i < length x.dims } + variant { length x.dims - i } + ensures { length result = length x.dims - i } + ensures { forall z. 0 <= z < length result -> my_nth result z > 0 } + = if i < (length x.dims - 1) then + let real_index = my_nth t i in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in + let y = div space (k.data (Cons real_index Nil)) + f in + Cons y (dY_shape_rec x s e k a t (i+1)) + else + let real_index = my_nth t i in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in + let y = div space (k.data (Cons real_index Nil)) + f in + Cons y Nil + + + let ghost function dY_shape (x : tensor 'a) (s e k a: tensor int) (t: list int) : list int + (*1D Index Tensors*) + requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*Normalize End*) + requires { forall ks. valid ks e.dims -> + let idx = my_nth x.dims (a.data ks) in + let step = k.data ks in + let e_value = e.data ks in + let res = if e_value < 0 then e_value + idx else e_value in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + (*K [C2]*) + requires { forall ks. valid ks k.dims -> k.data ks <> 0 } + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + (*Real Index t*) + requires { forall h. 0 <= h < length x.dims -> mem h t } + (*R6*) + requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks } + (*R7*) + requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks } + (*Y [C2]*) + requires { + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 + } + ensures { length result = length x.dims } + ensures { forall z. 0 <= z < length result -> my_nth result z > 0 } + = dY_shape_rec x s e k a t 0 + + + (*Given an axis, find the index of that specific axis representation in axis tensor*) + (*axis -> tensor normalized*) + (*x_rank -> rank of input X*) + (*i -> looking value*) + (*j -> current searching index*) + let rec ghost function find_j (axis: tensor int) (x_rank: int) (i: int) (j: int) : int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { x_rank = size axis.dims } + (*Rank > 0, see this better*) + requires { x_rank > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 } + requires { 0 <= i < x_rank } + requires { 0 <= j < x_rank } + requires { forall h. 0 <= h < j -> axis.data (Cons h Nil) <> i } + requires { exists z. 0 <= z < x_rank /\ axis.data (Cons z Nil) = i } + ensures { 0 <= result < x_rank } + ensures { axis.data (Cons result Nil) = i } + variant { x_rank - j } + = if j < (x_rank - 1) then + if axis.data (Cons j Nil) = i then + j + else find_j axis x_rank i (j+1) + else + if axis.data (Cons j Nil) = i then + j + else + absurd + + (*REVIEW THIS AXIOM*) + axiom axis_data_surjective: + forall axis: tensor int, x_rank: int, v: int. + length axis.dims = 1 /\ + x_rank = size axis.dims /\ + x_rank > 0 /\ + (forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank - 1) /\ + (forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) x_rank) = (mod (axis.data (Cons z2 Nil) + x_rank) x_rank) -> z1 = z2) /\ + 0 <= v < x_rank + -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v + + (*Builds a list with the real indices representation for each index in axis*) + (* axis = [2,3,1,0]*) + (* real_axis = [3,2,0,1]*) + (* axis -> tensor normalized*) + (* axis_rank -> rank of axis tensor = rank of input tensor*) + (* i -> current index in the axis tensor*) + let rec ghost function real_index_aux (axis: tensor int) (x_rank: int) (i: int) : list int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { x_rank = size axis.dims } + (*Rank > 0, see this better*) + requires { x_rank > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 } + requires { 0 <= i < x_rank } + requires { forall v. i <= v < x_rank -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v } + ensures { forall k. 0 <= k < length result -> + let z = i + k in + axis.data (Cons (my_nth result k) Nil) = z } + ensures { length result = x_rank - i } + variant { x_rank - i } + = if i < (x_rank - 1) then + let j = find_j axis x_rank i 0 in + Cons j (real_index_aux axis x_rank (i+1)) + else + let j = find_j axis x_rank i 0 in + Cons j Nil + + let ghost function real_index (axis: tensor int): list int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*Rank > 0, see this better*) + requires { size axis.dims > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < size axis.dims -> 0 <= axis.data (Cons z Nil) <= size axis.dims -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < size axis.dims /\ 0 <= z2 < size axis.dims -> + (mod (axis.data (Cons z1 Nil) + size axis.dims) (size axis.dims)) = (mod (axis.data (Cons z2 Nil) + size axis.dims) (size axis.dims)) -> z1 = z2 } + requires { forall v. 0 <= v < size axis.dims -> exists h. 0 <= h < size axis.dims /\ axis.data (Cons h Nil) = v } + ensures { forall k. 0 <= k < length result -> + let z = k in + axis.data (Cons (my_nth result k) Nil) = z } + ensures { length result = size axis.dims } + = let x_rank = size axis.dims in + real_index_aux axis x_rank 0 + + + let rec ghost function dX_coords_rec (x: tensor 'a) (coords t: list int) (s k a: tensor int) (i: int) : list int + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { length coords = length x.dims } + requires { 0 <= i < length coords } + ensures { length result = length x.dims - i } + + variant { length coords - i } + = if i < ((length coords)-1) then + let real_index = my_nth t i in + let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in + Cons original_index (dX_coords_rec x coords t s k a (i+1)) + else + let real_index = my_nth t i in + let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in + Cons original_index Nil + + + let ghost function dX_coords (x: tensor 'a) (coords t: list int) (s k a: tensor int): list int = + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { length coords = length x.dims } + ensures { length result = length x.dims } + dX_coords_rec x coords t s k a 0 + + let ghost function dslice (x : tensor 'a) (s k a : tensor int) (dy_shape t: list int) : data 'a + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { forall ks. valid ks dy_shape -> length ks = length x.dims } + (*proof*) + = fun ks -> + if valid ks dy_shape then + let x_coords = dX_coords x ks t s k a in + x.data x_coords + else x.background + + let ghost function opslice (x: tensor 'a) (s e k a: tensor int): tensor 'a + (*Additional dslice*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + positive y_shape /\ forall ks. valid ks y_shape -> length ks = length x.dims } + + (*Additional dY_shape*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + forall h. 0 <= h < length x.dims -> mem h t } + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + let s_normalized = normalize_start (x.dims) s.dims s a_normalized in + let e_normalized = normalize_end (x.dims) e.dims e a_normalized k in + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e_normalized.data (Cons real_index Nil) - s_normalized.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 } + + (*Additional normalize Axis*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks a_normalized.dims -> 0 <= a_normalized.data ks <= length x.dims -1} + (*Necessary because of real_index but redundant, maybe check real_index and change this*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall z. 0 <= z < size a_normalized.dims -> 0 <= a_normalized.data (Cons z Nil) <= size a_normalized.dims -1} + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks1 ks2. valid ks1 a_normalized.dims /\ valid ks2 a_normalized.dims -> + (mod (a_normalized.data ks1 + length x.dims) (length x.dims)) = (mod (a_normalized.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 } + (*Necessary because of real_index but redundant, maybe check real_index and change this*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall z1 z2. 0 <= z1 < size a_normalized.dims /\ 0 <= z2 < size a_normalized.dims -> + (mod (a_normalized.data (Cons z1 Nil) + size a_normalized.dims) (size a_normalized.dims)) = (mod (a_normalized.data (Cons z2 Nil) + size a_normalized.dims) (size a_normalized.dims)) -> z1 = z2 } + + (*Additional normalize Start*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length s.dims = length a_normalized.dims = 1 } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length x.dims = size s.dims = size a_normalized.dims } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + s ~ a_normalized } + (*Additional normalize End*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length e.dims = length a_normalized.dims = length k.dims = 1 } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length x.dims = size e.dims = size a_normalized.dims = size k.dims } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + e ~ a_normalized ~ k } + (*1D Index Tensors*) + requires {length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS - Se manter adicionar nos requires que estão para cima*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*A [C2]*) + (*requires { forall i. 0 <= i < length x.dims -> -length x.dims <= a.data (Cons i Nil) <= length x.dims -1}*) + requires { forall ks. valid ks a.dims -> -length x.dims <= a.data ks <= length x.dims -1} + (*A [C3]*) + (*requires {forall i j. 0 <= i < length x.dims /\ 0 <= j < length x.dims -> ( mod (a.data (Cons i Nil) + length x.dims) (length x.dims) ) = (mod (a.data (Cons j Nil) + length x.dims) (length x.dims)) -> i = j }*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> + ( mod (a.data ks1 + length x.dims) (length x.dims) ) = (mod (a.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 } + (*K [C2]*) + (*requires { forall i. 0 <= i < length x.dims -> k.data(Cons i Nil) <> 0 }*) + requires { forall ks. valid ks k.dims -> k.data ks <> 0 } + (*S [C2]*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks s.dims -> + let axis_index = a_normalized.data ks in + - (my_nth x.dims axis_index) <= s.data ks <= (my_nth x.dims axis_index) - 1 } + (*E [C2]*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks e.dims -> + let axis_index = a_normalized.data ks in + let step = k.data ks in + let e_val = e.data ks in + (step > 0 -> - (my_nth x.dims axis_index) <= e_val <= (my_nth x.dims axis_index))} + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks e.dims -> + let axis_index = a_normalized.data ks in + let step = k.data ks in + let e_val = e.data ks in + (step < 0 -> - (my_nth x.dims axis_index) - 1 <= e_val <= (my_nth x.dims axis_index) - 1) } + (*S [C3], E [C3], K [C3]*) + (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) > 0 -> s.data (Cons i Nil) <= e.data (Cons i Nil) }*) + (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) < 0 -> s.data (Cons i Nil) >= e.data (Cons i Nil) }*) + (*R6*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + forall ks. valid ks s_normalized.dims -> k.data ks > 0 -> s_normalized.data ks <= e_normalized.data ks } + (*R7*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + forall ks. valid ks s_normalized.dims -> k.data ks < 0 -> s_normalized.data ks >= e_normalized.data ks } + (*X [C2]*) + ensures { length result.dims = length x.dims /\ result.background = x.background } + (*Y [C2]*) + ensures { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + result.dims = y_shape } + ensures { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + result.data = dslice x s_normalized k a_normalized y_shape t } + (*proof*) + = let a_normalized = normalize_axis x.dims a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + + { dims = y_shape; data = dslice x s_normalized k a_normalized y_shape t; background = x.background} +end +*) +(* +module OPClip + use Tensor + use int.Int + use list.List + use Range + use real.Real + use real.MinMax + + + predicate is_scalar_tensor (t : tensor real) = + t.dims = Nil /\ t.data = (fun _ -> t.background) + + predicate ordered_bounds (l m y: tensor real) = + forall ks:list int. valid ks y.dims -> + (l.background <= m.background -> + l.background <= y.data ks /\ y.data ks <= m.background) + + predicate inverted_bounds (l m y: tensor real) = + forall ks:list int. valid ks y.dims -> + (l.background > m.background -> + y.data ks = m.background) + + let ghost function dclip (x l m : tensor real): data real + (*proof*) + = fun ks -> + if valid ks x.dims then + min m.background (max (x.data ks) l.background) + else x.background + + let ghost function opclip (x l m : tensor real): tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result ~= x } + ensures { ordered_bounds l m result } + ensures { inverted_bounds l m result } + (*proof*) + = { dims = x.dims; data = dclip x l m; background = x.background } + (*qed*) + + goal clip_correct: + forall x l m: tensor real. + is_scalar_tensor l /\ is_scalar_tensor m -> + let y = opclip x l m in + ordered_bounds l m y /\ inverted_bounds l m y + (* same as *) + (* + if l.background <= m.background then + ordered_bounds l m y + else + inverted_bounds l m y + *) + +end +*) \ No newline at end of file diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json new file mode 100644 index 00000000..73b56dab --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/proof.json @@ -0,0 +1,24 @@ +{ + "profile": [], + "proofs": { + "OPWhere": { + "dwhere": { "tactic": "split_vc", "children": [] }, + "opwhere": null + }, + "Range": { + "positive_size": null, + "positive_valid": null, + "size_append": null, + "size_push": null, + "valid_push": null + }, + "Tensor": { + "const": null, + "exteq": null, + "scalar": null, + "tensor": null, + "zero": null, + "zero_is_const": null + } + } +} diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml new file mode 100644 index 00000000..5f30ea5f --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak new file mode 100644 index 00000000..5f30ea5f --- /dev/null +++ b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3session.xml.bak @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz new file mode 100644 index 00000000..4799493a Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz differ diff --git a/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak new file mode 100644 index 00000000..4799493a Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/inputs/generic_tensors/tensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/meetings/formal_methods/minutes.md b/safety-related-profile/meetings/formal_methods/minutes.md index b27f0aa2..14992cc6 100644 --- a/safety-related-profile/meetings/formal_methods/minutes.md +++ b/safety-related-profile/meetings/formal_methods/minutes.md @@ -1,8 +1,171 @@ -# 2025/01/28 + +# 2026/02/18 + +See the list of [topics](./inputs/2026-02-18%20-%20Inputs.md) and [Loïc's notes](../formal_methods/slides/2026-02-18%20-%20Loic%20notes.pdf). + +# 2025/06/20 +## Agenda + +[Schéma global](./imgs/2025-06-20/image.png) + +- (Mariem, Salomé, Eric et Jean) Présentation pour retour critique, de trois formalisations ainsi que des problèmes rencontrés + - concat : concat (repo) + - Problèmes rencontrés : + - Complexité de la formalisation de l'opérateur : La nature "variadique" de l'opérateur combinée à la possibilité d'avoir des tenseurs de diverses dimensions + - Introduction de clauses de spécification formelle (requires, ensures) + - Appréhension des mécanismes de preuve + - Exploitation efficace (?) des modules préexistants, comme Sequences et Tensors + - conv 2D standard : repo (repo) + - Problèmes rencontrés + - Est ce qu'il y a des améliorations et/ou simplifications à proposer. + - Comment gérer les erreurs de preuve (Exemple: la fonction conv2d_output_value)? + - Est-il possible de généraliser le type des tenseurs pour éviter de réecrire la spécification pour le type real. + - graphe ONNX : [graph](../../documents/profile_formal/onnxgraph.mlw) (repo) + - Problèmes rencontrés : (voir [ici](#formalisation-du-graphe) en fin de ce document +- (Loïc) Preuves de l'existant + - Obligations de preuve générées par l'outil + - Propriétés utilisateurs + - Processus de preuve +- (Mariem, Salomé, Eric et Jean) Besoin de spécification de type "relationnel" (voir schéma ci-dessus) en plus de la spécification fonctionnelle + - Utile si la fonctionnelle n'est pas une redite de la relationnelle. + - Exemples : + - Utile pour concat + - Inutile pour where (l'opérateur traité lors de la première session). +- (Loïc) Processus de preuve spec fonctionnelle vs spec relationnelle +- Synthèse méthodologique +- Conclusion + + +### Formalisation du graphe + +Voir le code [WhyML](../../documents/profile_formal/onnxgraph.mlw). + +#### Q1: Choix de modélisation / implémentation + +Pour implémenter la fonction qui associe une valeur à un tenseur, j'utilise une liste de (clé, valeur). Est-ce un bon choix? + +#### Q2 : Bibliothèques d'implémentation + +Existe-t-il un "catalogue" d'implémentations des types de la bibliothèque standard? Par exemple, une implémentation de `Map` sur la base des listes? + +#### Q3 : Documentation et apprentissage + +Existe-t-il une liste de "bons exemples" dont nous pourrions nous inspirer? + +#### Q4: Démonstration simple qui n'aboutit pas... + +Soit le petit exemple suivant : +``` whyml +let rec fold_left (f: 'acc -> 'a -> 'acc) (acc: 'acc) (l: list 'a) : 'acc + variant { l } += match l with + | Nil -> acc + | Cons x xs -> fold_left f (f acc x) xs + end + +predicate all_true (l: list bool) = + forall b: bool. mem b l -> b = true + +lemma and_true_true: + forall a: bool, b: bool. Bool.andb a b -> a /\ b + +lemma fold_left_and_equiv_all_true: + forall l: list bool. + fold_left Bool.andb true l = true <-> all_true l +``` + +Quelle approche préconiserais-tu pour spécifier la fonction "fold_left" ? + +#### Q5 : Approche de preuve +Lorsqu'une preuve n'aboutit pas, il est difficle (pour un novice) de savoir comment procéder pour +- déterminer s'il s'agit d'une incapacité du prouveur à réaliser la preuve ou une réelle erreur de spécification / implémentation +- corriger l'erreur (de spécification ou d'implémentation)... +Quelle démarche doit-on suivre pour traiter une preuve qui n'aboutit pas? + +#### Q6 : Approche pour la preuve +Existe--il des constructions à éviter pour faciliter la preuve (et, réciproquement), existe-t-il des constructions à éviter (antipatterns? + +#### Q7 : Lien entre spécification et implémentation +Je souhaite spécifier les fonctions en utilisant les constructions les plus abstraites possibles. Par exemple, je défini l'état d'un graphe comme une application d'un ensemble de tenseurs vers un ensemble de valeurs. + +La déclaration est la suivante : +``` +type graph_state = Map.map tensor (option value) +``` +Je voudrais donc que ma spécification du comportement du graphe fasse référence à ce type abstrait. En pratique, la Map.map est implémentée au moyen d'une liste de (tenseur, valeur) : +``` +type fmap = list (tensor, option value) +``` +Je spécifie deux fonctions sur le type fmap: get et set. +- `fget_logic` +- `fset_logic` + +J'implémente ces 2 fonctions (`fget` et `get`). + +Je démontre que +- `fget` implémente bien `fget_logic` +- `fset` implémente bien `fset_logic` + +Dans ce cas, il n'y a pas de différence entre l'implémentation et la spécification. + +Maintenant, je souhaite établir le lien entre mon implémentation et la `Map` abstraite. + +J'introduis deux lemmes montrer que les deux fonctions `fget` (resp. `fset`) et `Map.get` (resp. `Map.set`) retournent toujours les mêmes valeurs. + +Est-ce la bonne approche? + +#### Q8: Problème de preuve + +Voir les lemmes `get_set_eq` et `get_set_neq` dans le code [WhyML](../../documents/profile_formal/onnxgraph.mlw). + + +# 2025/03/20 +## Agenda +- Tutorial by Loïc Correnson (CEA) on Why3 +- Documentation about Why3 can be found [here](https://www.why3.org/). +- For the tutorial, we will use [Why3find](https://git.frama-c.com/pub/why3find) which "Why3find "provides a collection of utilities to ease the development +of Why3 project". + +## Participants +Mariem, Loïc, Edoardo, Nicolas, Jean, Christophe G., Henri, Eric + +## Minutes +- The Why3 code (written by Loïc...) can be found [here](./code) +- Discussion about the role of Why3 in our process. See diagram below + - Why3 is used to specify the operators (and the graph execution semantics). For the moment, focus is placed on the operators. + - The specification may be relational or operational, depending on the operation. In some cases (e.g., `sqrt`) a relational specification (`sqrt(x)` is such that `sqrt(x)^2=x`) is simpler than the operational specification (which will describe how to compute the square root). + - In our case, most of the operations (not all) will need to be described operationally. + - We need to provide a test oracle to the user. + - The oracle may be an ocaml executable program (which can be generated automatically from some specs). + - It may also be the C code that can also be generated automatically from the spec if it has been written to allow this generation (see example of max value computation) + - The generated code is supposed to be compliant with the procedural specification since it has been generated from it (the code generator may still need to be qualified...) + - The generated C code may not comply with one's coding standard. It is up to him/her to refactor this code to make it comply with the coding rules. + - The modified C code may be proved to comply with the specification thanks to the FramaC toolchain. In that case, the ASCL spec may be directly imported from the Why3 spec. The loop invariants will still to be added manually... + - The FramaC part is not addressed by SONNX. + ![diagram](./imgs/2025-03-19/formal_spec_ver_phases.png) +- The specification must be kept as simple and readable as possible, with no fancy optimizations. + - The formal specification shall be as traceable as possible to the informal spec. (off-meeting: It may be the case that we have to formalize this traceability) + - In SONNX, we have limited the number of dimensions of tensors (e.g., 2 spatial dimensions for `conv`). This simplifies the implementation (e.g., the number of loop for convolution is bounded). (off-meeting: to we accept 1 dimension? if yes the code generator will have to handle this level of variability) +- As far as possible, we shall specify the operators as **total functions**, since it strongly simplifies the specification and proof effort (we don't have to demonstrate systematically that we are in the operator's domain). + - We consider that providing the `requires` clause is important because, at the end of the day, the operator will only be usable in the domain defined by these clauses (the functions will not be total). And we have to demonstrate that all levels of the specification comply with the top-most spec. + - We may favor total functions and still add `require` clauses in order to restrict the domain . +- Work on the formalization of tensors (see code). We tried two representations: + - a "flattened" representation in which tensor elements are "stored" in a mono-dimensional array. + - This representation raises strong difficulties when it comes to traversing all dimensions of the tensor. It also raises problem if the indexes are not in the domain allowed by the size of the tensor's array. So we moved to a second representation... + - a "hierarchical" representation in which the tensor elements are "represented" by a total function mapping an index to the value domain. This representation is based on the `seq`module that provides most of the needed constructs. +- Work on the `where` operator + - Note: a version of this operator has been generated by Henri using ChatGPT. We have not checked thoroughly if this specification was correct (it looks like...), but it remains interesting. Surprisingly, chatGPT seems to "know" Why3 pretty well and can be used as a means to generate part of a spec or explanations about existing Why3 code. +- Next steps: complete the work initiated during this meeting on the `where` operator: + - [ ] generate the ocaml code and build the test bench to be able to execute it + - [ ] build the procedural, C oriented, formal specification + - [ ] Prove the compliance of the C-spec to the top level spec + - [ ] generate the C code and build the test bench to be able to execute the spec + +# 2025/01/28 ## Agenda The objectives of the meeting are the following : -- Determine the approach for operator specification. On the basis of Loïc’s proposal: +- Determine the approach for operators and graphs specification. On the basis of Loïc’s proposal: * Option 1: use C code * The reference implementation (in C) is the specification. Compliance of an implementation to this specification will be demonstrated by testing with epsilon precision. No formal specification language (e.g., ACSL, Why3, etc.) is required. * Option 2: use Why3 @@ -11,10 +174,45 @@ The objectives of the meeting are the following : * Algebraic Properties: Show some good algebraic properties of the reference algorithm in Why3 (in ℝ) (e.g., linearity, commutativity, associativity, distributivity with respect to addition, existence of a neutral element, symmetry, etc.). * Option 3: use ACSL * Similar to the Why3 versions above, but using ACSL/Frama-C. - -- Determine the best approach for developing the reference specification for the Conv operator. +- Determine the formalism for operators specification +- Determine the process/worksharing for developing the operator (starting with CONV) + ## Participants +Edoardo, Loïc, Nicolas, Jean, Christophe Gar., Eric, Augustin, Mariem ## Minutes +This document summarizes the discussions and conclusions from our meeting on the specification approaches for ONNX operators and graphs. +### What is the purpose of the specification (ACSL or Why3)? What is the added value? Is the C reference algorithm not enough? (Eric) +- (Loïc) It depends on our needs. If we only require tests, then the C implementation is sufficient. However, if we need proofs, we require a specification in a formal language. +The specification cannot be written in C. It should either be a reference implementation (in Coq, Why3, etc.) or a mathematical specification. +- (Jean) ACSL specification is suitable for industrial purposes. +- (Eric) We have to ensure that what we propose is useful and useable for the type of targeted systems, operators, etc. If people develop fancy optimized implementations, proof will be complex, possibly out of the economically-viable domain with respect to the targeted development assurance level. +### What can we do with Why3 specification? +- Execute the specification, which may contribute to its validation. +- Generate C program that complies with the specification +- Generate coq program +- Provide the axiomatization to facilitate ACSL-level verifications (easier than doing everything in ACSL) +- Even if no formal proof is done, Why3 is an appropriate and efficient formalism to describe the operators' algorithms. +### What do we need to succeed with the Why3 specification? + - Developing libraries such as matrices, algebraic signatures, logical signatures, etc. would reduce the specification effort. + - Having a detailled Why3 description of those libraries are not mandatory in the first place. We may rely on C implementations to execute the spec. +### Is graph specification the same as operator specifications? +(Loïc) No, it is not the same. It is a separate subject and likely more complex than operator specifications. +The specification technique used for graph specification differs from that used for operator specifications, as we need to find a way to describe the graph. +### Conclusion on objectives 1 and 2 +- We will use the Why3 specification approach. This involves describing the reference algorithm of ONNX operators in Why3. +- From the Why3 implementation, it is possible to generate C programs and other implementations. +- This template can be integrated into Edge. +### How will we proceed with developing the reference operator (e.g., convolution or others)? +- We will organize a workshop in Toulouse in March. During this workshop, Loïc will provide all the necessary foundations and knowledge about Why3, and we will develop the reference operator together. +### Conclusion on objective 3 +- We will implement the specification of the reference operator together during a workshop to be held in Toulouse in March. +- The date of the workshop is to be determined. +### New Actions +[ ] (280125-01 - Mariem) Organize Why3 workshop (mid-march) +[ ] (280125-02 - Eric) Check how this work fits wit DeepGreen's objectives and check possible funding. +[ ] (280125-03 - All) Check if/how we could fund this work... + + # 2024/11/29 @@ -34,7 +232,7 @@ The objective of the meeting is to answer the following quesiotns: Dumitru, Nicolas, Christophe Gar., Mariem, Augustin, Jean, Eric [ed.] ## Minutes -- Supporting slides are available [here](./slides-29-11.pdf). +- Supporting slides are available [here](./slides/slides-29-11.pdf). ### Can the code be a specification? - For some simple operators, the code may be a sufficient specification. For others, the code may be more complicated (e.g., broadcasting). The combination of the informal specificaiton and the code can be sufficient. diff --git a/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf b/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf new file mode 100644 index 00000000..1bf047ef Binary files /dev/null and b/safety-related-profile/meetings/formal_methods/slides/2026-02-18 - Loic notes.pdf differ diff --git a/safety-related-profile/meetings/formal_methods/slides-29-11.md b/safety-related-profile/meetings/formal_methods/slides/slides-29-11.md similarity index 100% rename from safety-related-profile/meetings/formal_methods/slides-29-11.md rename to safety-related-profile/meetings/formal_methods/slides/slides-29-11.md diff --git a/safety-related-profile/meetings/formal_methods/slides-29-11.pdf b/safety-related-profile/meetings/formal_methods/slides/slides-29-11.pdf similarity index 100% rename from safety-related-profile/meetings/formal_methods/slides-29-11.pdf rename to safety-related-profile/meetings/formal_methods/slides/slides-29-11.pdf diff --git a/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z b/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z new file mode 100644 index 00000000..8eb131f3 Binary files /dev/null and b/safety-related-profile/meetings/general/2025-06-09 - MEET-UP/SONNX - Meetup 2025.7z differ diff --git a/safety-related-profile/meetings/minutes.md b/safety-related-profile/meetings/minutes.md index eed24832..90541bfe 100644 --- a/safety-related-profile/meetings/minutes.md +++ b/safety-related-profile/meetings/minutes.md @@ -1,3 +1,1775 @@ +# 2026/06/03 +## Participants +- Ricardo, João, Eduardo, Jean-Loup, Franck, Jean, Eric +## Agenda +- Review of actions (Eric) +- A quick look at the [local WG actions](../meetings/Local%20WGs/Tlse/agenda.md) +- A quick feedback on the use of Gemini to assist writing specs... +- Status of Graph spec (Ricardo) +- Calls for contributors? When and how? +- A quick feedback on Hypothesis +- Roundtable +## Minutes +- Short presentation of the use of Gemini to generate an informal spec. Application to **Resize** +- Graph spec: + - Abstract spec is OK + - Concrete spec is close to completion. J&R Thesis report will document the model design and proof. +- Call for contributors + - The number of participants to our WG meeting seems to be steady, but it is low. + - R&J will both leave to the project by the end of Jun, and this will be a great loss ;-(... + - We have already done several communications (at ERTS and DATE) and other are soon to come (two workshops in Lisbon by J&R and CTIC by Eric in Tlse). The Lisbon SAIV conf. may raise some interest in the academic community. + - The current outputs of the WG (guidelines, specs...) represent a good basis to embark other people. Concerning formal methods, for example, we have good examples that can be reused and adapted to create (some of) the remaining ones with a reasonable effort. + - Edoardo could possibly propose students project in Q4. + - We have to contact the industrialists that were involved at the beginning of the project, and other that have contributed heavily at some point (Airbus Helico, Thales AVS, etc.) + - See actions (0306-2) and (0306-3) below. + +## Actions +### New actions +- [ ] (0306-1, Jean) Update the spec of **Maxpool** (indices generation part). + - Do not refer to "upper" but "min". + - Modify the definition of "indices" to specify the tie break rule when multiple values are equal to the max... + - To be review on Friday 5th. +- [ ] (0306-2, Eric) Contact the industrialists that have shown some interest in ONNX (especially those that have expressed some specific needs in [scope.md](../deliverables/scope/scope.md)). +- [ ] (0306-3, Eric) Organize a (remote) presentation of our work to the community in Sept or Oct. +### Previous actions +- [X] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization + - We have to ensure that the number of arguments is correct. + - Conclusion : we keep things as is. +- [ ] (0804-1, Eric, local WG, Edoardo) Review Franck's work on **Div**, **Matmul**, **Tanh**. + - First [review by Eric](../sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md). + - Next review by local WG and Edoardo + - 3 things + - [ ] **guidelines** + - [ ] **applied guidelines on some ops (see above)** + - [ ] code to help generating the spec +- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool** + - Spec of second output of MaxPool by Mariem (concrete part to be completed) +- [X] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output... + - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J's and Jean's interpretation + - Check Jean's formula. + - Jean is currently updating the spec for the indices output. + - The spec has been updated. + - There is still a corner case to be sorted out... + - Spec to commit. +- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing. + - Jean is working on the test strategy. To be discussed during Toulouse's meeting. + - First draft by Eric reviewed by Jean-Loup. Corrections integrated, some comments still to be processed. Consolidation with existing doc to be done. + - Doc reworked +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) + - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...) +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. + +# 2026/05/20 +## Participants +- Ricardo, João, Eduardo, Jean-Loup, Franck, Jean, Eric +## Agenda +- Review of actions (Eric) +- Status of papers (Ricardo+João) +- Test strategy (Eric). See [doc](../sonnx/ops/docs/guidelines/tests-part2.md). +- An "idea" about formal spec... (eric) +- Status of AIDGE integration (Eric) +- Roundtable +## Minutes +- Discussion about the formal specification of generic properties of operators (symmetry, commutativity, neural element, etc.) + - This is an additional jutification for the effort on formal spec., in addition to providing the (potential) capability to develop a certified embedded code generator (that require a formal spec). + - Such properties have already been added in the formal spec by Ricardo and João. In their case, they are used as lemma to support other proofs.However, these lemma may be part of the verification / validation process of the formal specification. +- Discussion on testing + - There is a question about the pertinence of the use of Hypothesis: is it useful? does it facilitate the development of tests? + - The approach is to first complete the definition of our test strategy (based on equivalence classes) and then see if Hypothesis facilitates or not its implementation. + - Discussion on the equivalence class-based strategy. + - See Eric's [doc](../sonnx/ops/docs/guidelines/tests-part2.md). +- Formal spec and implementation of MaxPool + - The formal spec is complete for output Y what is missing is the specification of the indices output. This will be done once the problem with MaxPool is solved (see next point). + - To generate the code, the driver must be changed. To be discussed with Mariem. +- Discussion on the computation of indices for **MaxPool**. + - A new "bug" has been discovered, when the tensor is padedd and full of -inf. The index shall be taken in the tensor area, not in the padded area. In particular, this means that we shall not return the index of the top left most value of the padded tensor... +- Presentation of the latest result on the formalization of the graph (J&R) + - Some modification have been done so that the structure and the state of the graph are handled separately. + - Proof has been carried out to demonstrate that when a node is executed, then all its parent nodes (the nodes from which it depends) have been executed. + - Termination condition must be added: eventually, all nodes are executed. + - Question about the refinement: there is no real constraint about the refinement since we are not concerned with performance. So we have to chose the refinement that facilitate the verification and/or translation to C... +- J&R theses: complete by end of June!!!! +## Actions +### New actions +### Previous actions +- [X] (0605-1, J&R) Check the modeling of varargs operators +- [ ] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization +- [ ] (0804-1, Eric, local WG, Edoardo) Review Franck's work on **Div**, **Matmul**, **Tanh**. + - First [review by Eric](../sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md). + - Next review by local WG and Edoardo +- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool** + - Spec of second output of MaxPool by Mariem (concrete part to be completed) +- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output... + - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J and Jean + - Check Jean's formula. + - Jean i scurrently updating the spec for the indices output. +- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing. + - Jean is working on the test strategy. To be discussed during Toulouse's meeting. + - First draft by Eric reviewed by Jean-Loup. Corrections integrated, some comments still to be processed. Consolidation with existing doc to be done. +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) + - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...) +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. + +# 2026/05/06 +## Participants +- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Franck +## Agenda +- Review of actions (Eric) +- "Feeedback" on DATE 2026 +- Status of papers (Ricardo+João) +- ONNX meetup 2026 +- Test strategy +- Roundtable +## Minutes +- Paper submissions + - SAIV 2026 + - Notif. 15th May + - Overlay 2026 + - Notif. 25th May +- DATE 2026 + - See the workshop program at https://www.date-conference.com/workshop/w03 + - Very limited (and non satisfying...) number of attendees... +- Roundtable + - R&J: + - Modification of the graph modelling. + - Closer to the graph informal spec. + - Pb with varargs op. + - Eric: test strategy + - Mariem + - Work on AIDGE benchmarking module to compare SONNX vs. ONNX runtime + - Use of static allocation for CONV? + - Loïc proposes to pass the array as a parameter rather than use a local decl. + - Mariem modified the formal spec. + - Solution validated by R&J. + - Hamza + - Integration of LeakyRelu + Mul + Tanh in AIDGE +## Actions +### New actions +- [ ] (0605-1, J&R) Check the modeling of varargs operators +- [ ] (0605-2, J&R) Check if possible to avoid operator specific logic (arg checking) in the graph formalization +### Previous actions +- [ ] (0804-1) Review Franck's work on **Div**, **Matmul**, **Tanh**. +- To be done during next local meeting +- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool** + - Spec of second output of MaxPool by Mariem (concrete part to be completed) +- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output... + - **MaxPool** was incomplete because the informal spec not clear on the second output. Inconsistency between R&J and Jean + - Check Jean's formula. +- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing. + - Jean is working on the test strategy. To be discussed during Toulouse's meeting. + - First draft +- [X] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines + - Eric: done + - Jean-Loup: Done + - Edoardo: to be done by next week. +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) + - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...) +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. + +# 2026/04/08 +## Participants +- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Franck +## Agenda +- Review of actions (Eric) +- Roundtable +## Minutes +- Jean is working on the test strategy (based on equivalence classes). To be discussed during the next local meeting. +- J&R have completed the formal spec of **Maxpool**, except for the part concerning the indices output. See action. +- J&R have proposed a first formal spec of a graph. + - They have also specified pre conditions for the execution of operators. (i.e., a graph is executable iff its operators are executable). + - Discussion on the way to model the fact that an operator has been executed. In J&R model, this is modled using a "flag" attached to the operator. In Eric's, this was modeled as a property of the tensor... + - Discussion of the relation between the abstract and concrete spec of the operator execution order spec. + - The abstract spec is non-deterministic with regards to the execution order (any order compatible with the dataflow semantics is fine) whereas the concrete spec imposes *one* specific order. In that sense, the two specs are not equivalent, but the concrete one complies with the abstract one, which is the expected "result" +## Actions +- [ ] (0804-1) Review Franck's work on **Div**, **Matmul**, **Tanh**. +- [ ] (0804-2, Hamza) Complete R&J's formal spec on **MaxPool** +- [ ] (0804-2, J&R, Jean) Check the problem of the **MaxPool** informal spec on the indices output... +### New actions +### Previous actions +- [X] (2503-1, Eric, Amine) Organize meeting with Amine to setup a first +- application of the proposed framework. + - Meeting with Amine and Andryi on 07/04 +- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing. + - Jean is working on the test strategy. To be discussed during Toulouse's meeting. +- [X] (2503-3, Eric, Ricardo, João) Plan a meeting to review Eric's comments + - Done on 27/03 +- [ ] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines + - Eric: done + - Jean-Loup: Done + - Edoardo: to be done by next week. +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) + - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...) +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +- [X] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**. + - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides. + - **Div** to be done. + - Ready to be reviewed. +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [-] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Actions from local work sessions +- Cancelled. + +# 2026/03/25 +## Participants +- Eric, Mariem, Jean, Edoardo, Jean-Loup, Ricardo, João, Henri, Franck, Amine +## Agenda +- Review of actions (Eric) +- Submission of João and Ricardo's paper at SAIV'26 on formal verification +- Testing (eric) + - See [current guidelines](../sonnx/ops/docs/guidelines/tests.md) and some [additionnal material (draft)](../sonnx/ops/docs/guidelines/reviews/tests/tests_additional%20_material.md). +- Roundtable + - Presentation of the first work done on graph modeling (Ricardo & João) + - Latest news on the integration of operators in AIDGE (Henri) +- Welcome to Amine Abid from Saneaon. +- Local WG (Eric) + - See [actions](../meetings/Local%20WGs/Tlse/agenda.md). +## Minutes +- See agenda. +- Discussion on testing and equivalence-classes based testing. +- Amine presented his AI agentic framework. It could potentially be used to boost our activities by undertaking some of the specification or testing activities. A meeting may be useful to setup a first experiment. +## Actions +### New actions +- [X] (2503-1, Eric, Amine) Organize meeting with Amine to setup a first +- application of the proposed framework. + - Meetng with Amine and Andryi on 07/04 +- [ ] (2503-2, Eric) Consolidate a test strategy for SONNX out of the two sets of guidelines on testing. + - Jean is working on the test strategy. To be discussed during Toulouse's meeting. +- [ ] (2503-3, Eric, Ricardo, João) Plan a meeting to review Eric's comments +- [ ] ### Previous actions +- [ ] (1103-1, Eric, Jean-Loup, Jean, Mariem, Edoardo) Review Ricardo and João's formal specification and verification guidelines + - Eric: started but not yet finished... +- [X] (1103-2, all) Concludes on the expected behaviour of a **Conv** or **MaxPool** with incompatible sizes (should these conditions be verified statically? what should happen if these conditions cannot be guaranteed statically?) + - "If conditions cannot be ensured statically" (this point can be discussed), the violation of a precondition is considered as an error conditions and, in that case, the behaviour of the operator, must be described explicitly. +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) + - Use the github issue mechanism ("For feature requests or bug reports, please file a GitHub Issue"). (Note that there are 838 opened issues in total, 66 on the core runtime; maybe worth having a look to the list...) +- [X] (1103-4, all) Find a practical way to discriminate the "specifying" part of the informal specification from the informative part. + - From local WG: + - Introduce tags `[specx.y...]` and `[/specx.y...]`, where `x`, `y`,... are numbers, when a specific element in the section needs to be designated. The dot notation (`x.y...`) is introduced when a more specific element needs to be identified within an element that is already tagged. + The exact tag is : `[spec1]
` + - Elements that are provided for information are tagged using `[info]` `[/info]`. All other elements are specifying. See the spec of operator [**Div**](../sonnx/ops/spec/informal/div/div.md). + - Use section names when traceability is a the scale of the section. (i.e., the complete section would be enclosed in `[specx.y...]` and `[/specx.y...]`). +- [X] (1103-5, Mariem) Check why the SONNX backend has not been pushed in the AIDGE repo. +- [X] (1103-6) Ensure that the **Conv** op integrated in Aidge is actually the one generated out of Ricardo and João's formal spec. + - It is currently the first version. +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**. + - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides. + - **Div** to be done. +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Actions from local work sessions + +# 2026/03/11 +## Participants +- João, Ricardo, Hugo, Edoardo, Franck, Mariem, Henri, Jean, Eric +## Agenda +- Review of actions (Eric) +- Status of [specification](https://github.com/users/ericjenn/projects/4) +- Progress of João and Ricardo's work +- The case of **MaxPool**, continued... (Jean) +- Local WG (Eric) + - See [actions](../meetings/Local%20WGs/Tlse/agenda.md) +## Minutes +- João and Ricardo + - have completed the formal specification and verification guidelines (to be reviewed) + - have completed the formal spec of **Conv**, **Reshape**, **Flatten** + - are making progress on the extension or reals with IEEE special values + - have created the formal specification of complete (simple) networks and + - proved some properties on them (Lipschitz). + - generated a C implementation that can be executed and used as a reference implementation to verify some implementation (this is a simpler way than specifying a generic graph executor) +- Jean: presentation of the new problem found on MaxPool when the size of the kernel is larger than the size of the argument... +- Jean: We should clearly discriminate the formal part fo the specification for the informative part. + - This could be done by + - numbering paragraphs (not easy with markdown...) + - adding tags to the specifying paragraphs (a lot of tags) + - using a specific font / color to discriminate the informative part, as follows : + -

This is an informative part

+## Actions +### New actions +- [ ] (1103-1, all) Review Ricardo and João's formal specification and verification guidelines +- [ ] (1103-2, all) Concludes on the expected behaviour of a **Conv** or **MaxPool** with incompatible sizes (should these conditions be verified statically? what should happen if these conditions cannot be guaranteed statically?) +- [ ] (1103-3, all) Inform ONNX Runtime about the problem found in their implementation (**MaxPool** and others) +- [ ] (1103-4, all) Find a practical way to discriminate the "specifying" part of the informal specification from the informative part. +- [ ] (1103-5, Mariem) Check why the SONNX backend has not been pushed in the AIDGE repo. +- [ ] (1103-6) Ensure that the **Conv** op integrated in Aidge is actually the one generated out of Ricardo and João's formal spec. +- [ ] (1103-7) Complete tests on **Conv** as they have been done on **MaxPool** (after action 1103-2 is closed) +### Previous actions +- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**. + - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides. +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Actions from local work sessions + +# 2026/02/25 +## Participants +- Joõ, Camille, Eric, Mariem, Dumitru, Ricardo, Franck +## Agenda +- Review of actions (Eric) +- Status of [specification](https://github.com/users/ericjenn/projects/4) +- Meeting on accuracy (Eric+Jean+Franck), see the [minutes](../meetings/numerical%20accuracy/2026-02-17%20-%20DeepGreen%20-%20Accuracy.md). +- Meeting on formal methods (Eric+All) +- Progress and questions from Portugal! (Ricardo and João) +- The case of **MaxPool**, continued, see the [Jupyter notebook](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/tests/maxpool/Test_Max_Pool_Divergence.ipynb) (Henri) +- On the spec of mathematical operators, using [mpfr](https://www.mpfr.org/mpfr-current/mpfr.html#index-mpfr_005fpowr) (Eric) +- Local WG (Eric) + - See [actions](../meetings/Local%20WGs/Tlse/agenda.md) +## Minutes +- Ricardo and João have explained their approach to specify **MatMul**. Having a written version of the general principles, methods (and possibly tricks...) that have been applied would be extremely useful for the team. Hopefully, this could be part of the R&J's master's thesis report. +- On ORT, the behaviour of **Matmul** differs when applied on float null tensors and integer null tensors. Multiplying null tensors is well defined and can lead to non null tensors. We have to add a notice explaining that the behaviour of ORT differ from the spec. +- More generally, we agree that the spec must provide a unique output even though different implementations may return different result. We will add a notice stating the possible divergence (limited to ORT). It is up to an implementer to show where his/her implementation does not comply with the spec. We may produce some kind of a "compliance report" for ORT CPU (for instance) as an example. +- Back to **Maxpool**: + - we have to take care of optional outputs. + - the behaviour of **Maxpool** is extremely variable depending on the backend (CPU, tensortRT, CUDA). Besides the erroneous indexes when dealing with Infs, we also observe strange results on the max value themselves. Refer to [Henri's notebook](../sonnx/ops/spec/tests/maxpool/Test_Max_Pool_Divergence.ipynb) for further details. +## Actions +### New actions +### Previous actions +- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**. + - (Franck) Bounds for propagation **Tanh** and **Div**. About **Matmul**, see Franck's slides. +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [X] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available) + - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md) +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- [ ] Actions from local work sessions + - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - To be discussed during Feb session with Loïc. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + - On-going: First proposal sent by J&R ; currently being discussed with Jorge then to be discussed with Loïc + +# 2026/02/11 (shortened to 1h) +## Participants +- Henri, Ricardo, João, Jean, Mariem, Edoardo, Jean-Loup, Franck +## Agenda +- WG progress +- The case of MaxPool +- Accuracy analysis approach (Franck) +- News from DeepGreen (Mariem) + +## Minutes +- News + - Our work has been presented to the ERTS 2026 conference. The slides are [there](./Other_meetings/SONNX%20-%20ERTS2026.pdf). +- Status of actions (Eric) + - See also [those for the local WG](./Local%20WGs/Tlse/agenda.md). +- Work done by the Tlse WG and actions (Eric) + - See also [those for the local WG](./Local%20WGs/Tlse/agenda.md). +- Discussion about the section on accuracy (see supporting slides) + - We have to provide the user some information to support the analysis of error, including propagated error. We provide the expression of the propagated error at first order. + - Examples are being developed (Franck) on **Div**, **Matmul**, and possibly **tanh**. + - (Note that the accurracy section shall be named `_acc.md` and be placed along with the informal specification (`op.md`). ) + +## Actions +### New actions +- [ ] Provision of the accuracy section for **Div**, **Matmul**, and possibly **tanh**. +### Previous actions +- [X] (2801-1, eric) Check why Jean-loup cannot change the project's kanban +- [X] (2801-2, all) Add a disclaimer on the tests +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [X] (2801-4, Eric) Add a question to Loïc about the handling of mathematical operators in the formal spec; Floats are basically handled as real extended with infs and NaN. For the rest, the semantics is that of the reals. +- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available) + - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md) +- [X] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions + - Planned on Feb 18th, 14:00-16:00 +- [X] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC + - Subject proposed by Jean-Baptiste on 2026/01/16 + - Comments returned by Eric on 2026/01/19 + - Waiting for Jean-Baptiste reply... +- [X] (0312-1, Edoardo, Mohammed) Review of the broadcast operator + - Completed after Edoardo's comment have been taken into account. +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Action from local work session + - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - To be discussed during Feb session with Loïc. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + - On-going: First proposal sent by J&R ; currently being discussed with Jorge then to be discussed with Loïc + +# 2026/01/28 +## Participants +- Hugo, Henri, Ricardo, João, Jean, Mariem, Edoardo, Jean-Baptiste, Jean-Loup +## Agenda +- Review of actions [Eric] +- Review of project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8) +- Finalization of Clip spec. [João, Ricardo] +- Discussion about the "Generic Tensor Module and Potential Extension to Operators" [Ricardo and João] +- Discussion about the "Formalization of Purely Structural Operators" [Ricardo and João] +- Discussion about the formalization of mathematical ops [Henri] +- Remarks from Adrien (Airbus)[Jean] +## Minutes +- Finalization of Clip spec. [João, Ricardo] + - We keep the 3 "ifs" and we add a remark that the 2nd and 3rd can be synthesized as min(m...max(...)). +- Generic Tensor Module and Potential Extension to Operators + - We use a generic type and provide the necessary operator on type T. + - Where op: use int tensor for booleans... +- Formalization of Purely Structural Operators + - Data are copied "naively" (no move) +- Question on $tanh$ + - There are multiple ways to *implement* a $tanh$, with variable accuracy. In reality, we are not really specifying the floating point version of the operator (e.g., we are not specifying the expected accuracy): we are specifying the operator according to the semantics of real number + infinities and NaNs. In this context, the 3 versions of the $tanh$ (let's say I1, I2 and I3 see spec) are equivalent. However, we **know** that this is not true (i.e., I1 =/= I2 =/= I3). If we want to promote one implementation rather than another, we have to write the spec for that implementation. + - Off meeting (eric): in that case, we should not use "x" for the multiplication, but "x." (for instance), to refer to the multiplication with floating point numbers. Otherwise, if we propose one spec, let's say $a+b$, any one can interpret it as "b+a" because, in the domain of real numbers, these two forms are strictly equivalent. + - Remarks from Adrien + - In "**Informal** specification", the adjective "Informal" is negatively connoted: we should use "Specification". + - The test provided by SONNX shall **not** be considered as complete in the sense of the certification objectives. They are provided "as is" for the purpose of debugging, early testing, etc. We should add a "disclaimer" in the doc. + - Other + - Focus shall be placed on the operators in the "scope" list (esp. Airbus' and Thales' who contibutes significantly to the work... +## Actions +### New actions +- [ ] (2801-1, eric) Check why Jean-loup cannot change the project's kanban +- [ ] (2801-2, all) Add a disclaimer on the tests +- [ ] (2801-3, all) Check how NaNs are addressed in the completed operators. They shouldn't be treated as "errors". Update the guidelines accordingly. +- [ ] (2801-4, Eric) Add a question to Loïc about the handling of mathematical operators in the formal spec; Floats are basically handled as real extended with infs and NaN. For the rest, the semantics is that of the reals. +### Previous actions +- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available) + - Material shall be placed [here](https://github.dev/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/formal_methods/inputs/inputs-2026-02-18.md) +- [X] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions + - Planned on Feb 18th, 14:00-16:00 +- [X] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC + - Subject proposed by Jean-Baptiste on 2026/01/16 + - Comments returned by Eric on 2026/01/19 + - Waiting for Jean-Baptiste reply... +- [X] (0312-1, Edoardo, Mohammed) Review of the broadcast operator + - Completed after Edoardo's comment have been taken into account. +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Action from local work session + - (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - To be discussed during Feb session with Loïc. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc + +# 2026/01/14 +## Participants +*To be completed* +## Agenda +- Review of actions [Eric] +- Review of project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8) +- Discussion about issues raised by João and Ricardo + - Support for Integer Data Types in Tensor Modules + - Use generic types (clone a generic version of Tensor) + - [ ] (1401-1) Mariem: provide what has been done on the use of generic types and cloning... + - Formalization of empty tensors (C-Level) + - [X] (1401-2, Eric) Check how ONNX behaves with respect to empty tensors (is it possible to create an empty tensor using OP1 and resurrect it using OP2) + - It is not possible, see the paragraph about "null tensors" in the [glossary](../sonnx/ops/spec/informal/common/definitions.md). + - Memory Allocation and C-Code Behavior + - Consensus: use **parameter** + - cdrivers + - [X] (1401-3) Find a reference to justify the precedence order of `max a b + c = max (a,b) + c` + - The precedence is defined in the Why3 manual. For `max`, the rule about precedence of functions applies. See picture below + ![alt text](./attachments/why3_op_prio.png) + - NaNs representation in Why3 + - The high-level spec should match with the informal spec. So, if there are NaNs and Inf in the informal spec, then we should have NaNs and Infs in the high-level spec too. The refinement (high=>low) should focus on the structure of tensors not on the operations. In addition, we have to be clear that the formal spec does not model floating point but just R extended with special values. + The interpretation of the two levels of spec must be discussed and validated with Loïc (esp. with respect to special values...). + - Precedence Order in Operations +- Status of activities on AIDGE [Mariem] + - See [slides]() +- Next meeting local WG (Toulouse) + - Agenda is [there](./Local%20WGs/Tlse/agenda.md) +- Discuss opportunity to present our work in CTiC? +- Review of [ONNX IR](https://github.com/onnx/onnx/blob/main/docs/IR.md). Who? +- Announces + - [DATE 2026 workshop](./attachments/date2026_workshop.pdf), April 20-22, Verona, Italy + - [ERTS 2026](https://conference-erts.org/), February 5-6, Toulouse, France + - [Certification Together (CTiC)](https://www.certification-together.com/) +## Minutes +*To be completed* +## Actions +### New actions +- [ ] (1401-4, All) Collect all questions to be asked to Loïc with enough material to present the issues (example) and give our own solution (when available) +- [ ] (1401-5, Eric) Plan a meeting with Loïc to sort out the list of questions +- [ ] (1401-6, Eric, Jean, Jean-Baptiste) Discuss a possible subject to be presented at CTiC +### Previous actions +- [X] (1712-1, Joao, Ricardo) Give examples of values leading to discrepancies with operator **Range** +(see Range folder review) + - L=6459650.5, M=1928958.0 => 8388608.0 in fp32 + - L=6459650.5, M=1928958.0 => 8388608.5 in fp64 + - 8388608 is $2^23$. At this magnitude, ULP=1 (minimum distance between two successive values) hence 2^23+0.5=2^23, 0.5 is absorbed. +- [X] (1712-2, Eric, Jean) Check opportunity to present our work at CTIC + - Date: June 17th-19th 2026, Toulouse, see [CfP](https://www.certification-together.com/index.php/call-for-papers/) + - Abstract or proposal for workshop expected before mid-March 2026 + - Our work will be presented at ERTS2026 (in **February**) and (partially) DATE2026 (in April). Would it make sense to present an update to CTIC in **June**? + - Jean: we have to focus on certification => If we have something to present on (e.g, ARP). Jean-Baptiste: we have already some material => To be discussed. +- [ ] (0312-1, Edoardo, Mohammed) Review of the broadcast operator + - Edoardo : Done, analyzed by Jean-Loup, still some point to be analyze / discussed + - Mohammed : To be done during Jan. 2026 +- [ ] (1911-2, All) Review Jean's V&V proposal + - Eric to put updated slides on the repo : Done. + - File to be reviewed not yet delivered. +- Action from local work session + - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc + + +# 2025/12/17 +## Participants + - _To be completed_ +## Agenda +- Review of actions [Eric] +- Project status ([Kanban](https://github.com/users/ericjenn/projects/4/views/8) +- Sync of the project Kanban (esp. formal specs) +- Discussion about João and Ricardo's issues: + - On **Range** (pb with FP), + - On **Reshape** (shape inference) +- Status of formal spec of **Conv** (and FP values modeling) +- Numerical accurracy, the case of [**Conv**](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/sonnx/ops/spec/informal/conv/conv.md) + - Separation of accuracy from the rest of the spec (e.g., "conv_acc.md"). +- About guidelines + - for formal spec... + - for numerical precision... +- About the opportunity to organize an "event" in Feb. or March 2026 + - Objective: present the status of our activity to the community + - Have **at least** one full example covering: informal spec, tests, formal spec (+proofs), generated code, integration in Aidge (ideally, a simple graph...) + - Have all guidelines : informal (OK), formal (todo), numerical accuracy (todo), tests (todo) +- About contacts to increase participation... +## Minutes + - See agenda. + - Possibe opportunity to present our work at the CTIC conf. (CfP in Jan. 2026). +## Actions +### New actions + - [ ] (1712-1, Joao, Ricardo) Give examples of values leading to discrepancies with operator **Range** + - [ ] (1712-2, Eric, Jean) Check opportunity to present our work at CTIC +### Previous actions +- [ ] (0312-1, Edoardo, Mohammed) Review of the broadcast operator + - Edorardo : Done + - Mohammed : To be done during Jan. 2026 +- [X] (0312-2,Ricardo, Joao) Open PR on unsqueeze and flatten +- [X] (0312-3,Jean) Update the project's [Kanban](https://github.com/users/ericjenn/projects/4/views/8) to reflect the actual status of SONNX... +- [X] (1911-1, Jean) Put the contents of the [presented slides](./slides/2025-11-20-Jean-verification-plan.pdf) into some nice markdown file, and put it in [guidelines area](../sonnx/ops/docs/guidelines/) for review + - Eric to put updated slides on the repo : Done. +- [ ] (1911-2, All) Review Jean's proposal (see above) + - File to be reviewed not yet delivered. +- [X] (0511-3, Eric) Give access to the SONNX github "project" in order to facilitate the management of the artifacts statuses. +- [X] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip + - There is a dependency to the execution platform. The test setup must be described along with the test script. + - [X] Check if there is a possible way to set the CPU provider so that test pass. + - When running the script, the provider has to be chosen (with different types) +- Action from local work session + - [X] Modify existing operator specifications to comply with new conventions. + - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.) + - [-] (Mariem) Give R&J a feedback on the formal spec (in particular: recall a few principles to be followed). + - Cancelled + - [X] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$) + - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + - On-going: First proposal sent by J&R ; currenlty being discussed with Jorge then to be discussed with Loïc + +# 2025/12/03 +## Participants + - Eric, João, Jean-Loup, Edoardo, Ricardo Silva, Mariem, Franck, Jean, Nicolas, Hugo, Mohammed, Henri. +## Agenda +- Review of actions [Eric] +- DATE 2026 workshop 20-22 april, Verona (❤️) [Eric] +- Status of SONNX as of end 2025 [Eric] + - Where should we be? + - Where are we? + - Where do we want to go now? + - How do we go there? + - Funding... proposal by IRT +## Minutes +- See agenda ;-) +## Actions +### New actions + +- [ ] (0312-1,Edorado, Mohammed) Review of the broadcast operator +- [ ] (0312-2,Ricardo, Joao) Open PR on unsqueeze and flatten +- [ ] (0312-3,Jean) Update the project's [Kanban](https://github.com/users/ericjenn/projects/4/views/8) to reflect the actual status of SONNX... + +### Previous actions +- [ ] (1911-1, Jean) Put the contents of the [presented slides](./slides/2025-11-20-Jean-verification-plan.pdf) into some nice markdown file, and put it in [guidelines area](../sonnx/ops/docs/guidelines/) for review + - Eric to put updated slides on the repo : Done. +- [ ] (1911-2, All) Review Jean's proposal (see above) + - File to be reviewed not yet delivered. +- [ ] (0511-3, Eric) Give access to the SONNX github "project" in order to facilitate the management of the artifacts statuses. +- [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip + - There is a dependency to the execution platform. The test setup must be described along with the test script. + - [ ] Check if there is a possible way to set the CPU provider so that test pass. +- Action from local work session + - [ ] Modify existing operator specifications to comply with new conventions. + - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.) + - [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed). + - [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$) + - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - Some solutions are available. These solutions must be discussed with Loïc. + - Solution to be sent to Mariem first... + +# 2025/11/19 +## Participants + - Eric, Edoardo, Franck, Ricardo Silva, João Machado, Henri, Mariem, Jean-Loup, Mohammed, Jean, Andreas +## Agenda +- Review of actions [Eric] +- Review process [Jean] +- Status on broadcasting [Jean-Loup] +- Back to the factorization of operators... +- Fun facts + - The case of NaNs in Clip [Ricardo and Joao] + - The case of empty tensors... [Ricardo and Joao] (see [here](https://onnx.ai/onnx/repo-docs/IR.html#tensor-definition)) +- Management of work using github's "project management" [Eric] (see [here](../sonnx/ops/docs/guidelines/lifecycle.md)) +## Minutes + - All the above items minus "broadcasting" and "fun facts" and (to be addressed during next meeting). + - Jean's slides can be found [here](./slides/2025-11-20-Jean-verification-plan.pdf) + - Franck's slides can be found [there](./slides/2025-11-20-Franck-numerical-accuracy.pdf) + - In order to involve more people, we also have to provide more guidelines. This includes (procedure + example) + - Guidelines on test using + - Guidelines on formal specification and proof + - Guidelines on specifying numerical errors + - Franck's slides already provide a good example +## Actions +### New actions +- [ ](1911-1, Jean) Put the contents of the [presented slides]() into some nice markdown file to be put in [guidelines area](../sonnx/ops/docs/guidelines/) +- [ ](1911-2, All) Review Jean's proposal (see above) +### Previous actions +- From work session + - [ ] Modify existing operator specifications to comply with new conventions. + - Ensure that all existing operators (and pseudo-op such as $bc$) handles tensors with null dimensions correctly. (To be added in the guidelines.) + - [X] (Eric) Create a side note about "empty tensors" (To be placed in "doc"). + - Not necessary: tensors (including scalar and empty tensors) are well-defined in the ONNX IR documentation ([here](https://onnx.ai/onnx/repo-docs/IR.html#tensor-definition)) + - [X] (Mariem) Give R&J a pointer to Why3 where NaN are handled. + - [ ] (Mariem) Give R&J a feedback ont the formal spec (in particular: recall a few principles to be followed). + - [X] (Jean-loup) Separate the spec of the pseudo op broadcasting and the max operator + - [X] (Eric) Provide explanations about the new way to manage modifications (using Pull Requests). + - See this [note](../sonnx/ops/docs/guidelines/lifecycle.md) + - [ ] Check the display problem with LaTeX formulae in Markdown (see $\text{Add}$) + - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. +- [X] (0511-2, Jean) Provide a first draft of the document about verification. +- [ ] (0511-3, Eric) Give access to the SONNX github project in order to facilitate the management of the artifacts statuses. +- [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip + - Eric : + > See ORT issue #15304 that was asking for the support of int32, uint32 for Clip. Solved by #15306, so ORT supports at least `uint32` and `int32`. + > Seems to work on Google Collab: + - ![alt text](./attachments/image.png) + - Ricardo & João: + > We found this, onnxruntime/docs/OperatorKernels.md at main · microsoft/onnxruntime. + > And for CPUProvider the following types are not supported: INT16, UINT16,BFLOAT16. + > We were following ONNX documentation Clip - ONNX 1.21.0 documentation and + > we didn’t expect that some types are only supported by specific providers. + > For example, int16 and uint16 are not supported by CPUProvider but are supported by DmlExecutionProvider. + > BFloat16 is said to be supported by ONNX, although we didn’t find any provider that does so. + > Apparently, this is no longer a doubt but we will have to check both these documentation to ensure that our provider(CPUProvider) supports the respective types. In this context the test generation will also depend on the provider being used + +### Past actions +- [-] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). + - Action cancelled. + + +# 2025/11/05 +## Participants +Jean, Eric, Mariem, Ricardo, João, Jean-Loup, Franck, Dumitru, Henri, Jean-Baptiste, Mohammed. + +## Agenda + - Review of actions [Eric] + - Status on repo organization [Mariem] + - Status on operator informal and formal specs [All] + - Discussion about setting up a real and clear management of item status (update the existing [table](../meetings/operator_spec_sub_wg/worksharing.md), use Github features?) [All] + - A few words about the integration in AIDGE? [Mariem] + - Meeting on graph specification [Eric] + - Next work session... + - Discussion of the overall V&V strategy [Jean] +## Minutes + - Status on repo organization: operators have been moved. Problem to generate the C code fo the where op (Loïc will have a look at this) + - Jean, Eric, Marie: DIV, ADD, MUL, SUB / SOFTMAX, PAD, RELU + - João, Ricardo: CLIP (update + hypothesis + formal: pb with NaNs), SLICE (informal, hypothesis, formal at high level, working on the proof) +## Actions +### New actions + - [ ] (0511-1, Joao, Ricardo) Check how to handle NaN in Why3 (if possible!)... See Mariem's link. + - [ ] (0511-2, Jean) Provide a first draft of the document about verification. + - [ ] (0511-3, Eric) Give access to the SONNX github project in order to facilitate the management of the artifacts statuses. + - [ ] (0511-4, João, Ricardo, Eric) Investigate the problem of Clip + +### Past actions +- [X] (2210-1, Mariem) Write a "readme.md" to explain the (new) organization of the repo (for the SONXX products) +- [X] (2210-2, Mariem) Move all operators to the new location +- [X] (2210-3, Eric, Jean-Baptiste, Jean) Review Hypothesis-based test cases description from João and Ricardo (on **conv**) + - Eric: [Done](../documents/onnx/ops/spec/informal/conv/reviews/eric_tests.py) +- [X] (2210-4, Eric, Jean, Mariem) Review João and Ricardo's work on the operator **clip** + - [Done](../documents/profile_opset/clip/reviews/jean-eric.md) +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +### Long term actions +- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + + + +# 2025/10/22 +## Participants + Jean, Eric, Franck Védrine, Ricardo Silva, Edoardo Manino, João Machado, Hugo, Jean-Baptiste Rouffet, Mariem, Henri Belfy, Jean-Loup Farges et Nicolas Valot +## Agenda + - Events + - Technical presentation to WG114 on Oct. 16th ([see slides](./Other_meetings/SONNX%20-%20WG114-%20oct-2025.pdf)) + - Poster at Mobilit'AI (Toulouse) + - Review of actions [Eric] + - Status on repo organization [Mariem] + - Status of testing activities [João, Ricardo] + - How to extend this work? + - Other activities + - Local activities on operators [Eric, Jean, Mariem] + - Meeting on graph [Eric] +## Minutes + - See agenda. + - Jean-Baptiste attended the Mobilit'AI conference. He met people interestied by our work. They were wondering how we will ensure compliance between the design model (e.g., Pytorch) and the exported SONNX model and between the SONNX model and its implementation for a given target... These are very good question that we do not really address in SONNX. This may be something to be done collectively on the basis of some existing, open source training and implementation framework (such as CEA's [AIDGE](https://projects.eclipse.org/projects/technology.aidge) platform.). + - Eric presented the SONNX work to the WG114. There was a great interest on the numerical aspect. Providing a working example of the approach developed by Franck would be very appreciated... + - Ricardo and João have completely covered operator **clip** (from informal spe to formal with proof)... That's really GREAT!!! Kudos to those extremely efficient and proactive students (and their advisers)!!! + - Jean propose that Ricardo and João next operator be one common to Thales and Airbus' use cases (e.g., **maxpool**, **relu**, etc.). + - Jean-Loup's working on operator **max**. He is addressing the question of broadcasting. For the moment, the broadcasting operating may be specified for **max**. if it can be "factorized" (i.e., be expressed in such as way it can apply to other operators), we will do it later. + - A meeting on the graph semantics is planned on Friday. It was initially planned to address the formal specification of an ONNX graph. I (eric) propose that we shorten the meeting and address, first, the informal specification of a graph, including control flow operators (such as **loop** and **if**...). +## Actions +### New actions +- [ ] (2210-1, Mariem) Write a "readme.md" to explain the (new) organization of the repo (for the SONXX products) +- [ ] (2210-2, Mariem) Move all operators to the new location +- [ ] (2210-3, Eric, Jean-Baptiste, Jean) Review Hypothesis-based test cases description from João and Ricardo (on **conv**) +- [ ] (2210-4, Eric, Jean-Mariem) Review João and Ricardo's work on the operator **clip** +### Past actions +- [X] (0810-1, all) Review [Jean-Baptiste's document on ED324](../analyses/certification/SONNX_ED324_interest.docx) + - Eric comments (in the doc) +- [X] (0810-2, all) Review [Henri's spec of DIV](../documents/profile_opset/div/div.md) + - See work done with Mariem and Jean on div, mul, add... +- [X] (0810-3, all) Review [Mariem's proposal](tbc) for a new repo +- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting. + - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks? + - Waiting for Eric +- [X] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat` +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +### Long term actions +- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + + +# 2025/10/08 +## Participants + *To be completed* +## Agenda + - Presentation on MLIR/ONNX-MLIR by [Alexandre EICHENBERGER](https://research.ibm.com/people/alexandre-eichenberger). Alexandre's presentation is available on [LFX](https://openprofile.dev/my-meetings). + - New organization of repo [Mariem] + - Feedback on testing activities [Ricardo,João], see their [reviews](../documents/profile_opset/conv/reviews/review-joao-ricardo.md) + - Review of actions [Eric] + - Feedback on SONNX <=> ED 324 traceability [Jean-Baptiste], see [his document](../analyses/certification/SONNX_ED324_interest.docx) +## Minutes +See agenda and new actions +## Actions +### New actions +- [ ] (0810-1, all) Review [Jean-Baptiste's document on ED324](../analyses/certification/SONNX_ED324_interest.docx) +- [ ] (0810-2, all) Review [henri's spec of DIV](../documents/profile_opset/div/div.md) +- [ ] (0810-3, all) Review [Mariem's proposal](tbc) for a new repo +### Past actions +- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting. + - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks? + - Waiting for Eric +- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat` +- [X] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability + - In progress. To be reviewed during next meeting. +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +### Long term actions +- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + + +# 2025/09/24 +## Participants + *To be completed* +## Agenda + - Review of actions + - News about work on formal specs [Mariem, Salomé] + - Presentation "Using Hypothesis to test SONNX operators" [João + Ricardo] + - Status on [contributions](./operator_spec_sub_wg/worksharing.md) +## Minutes +See agenda. +## Actions +### New actions +No new action. +### Past actions +- [ ] (1009-1, Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting. + - Among the questions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks? + - Waiting for Eric +- [X] (1009-2, Eric) Mail to be send on SONNX mailing list for contributions + - Request sent: no answer... +- [X] (2708-1, Eric) Give short guidelines about error / failure conditions. + - See minutes of 2025/08/27 meeting. + - Done. +- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat` +- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability + - In progress. To be reviewed during next meeting. +- [-] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) + - Cancelled +- [X] (1806-4, Eric) Organize a "physical" working session on the graph specification + - Poll link sent during meeting. Possibility to organize a "physical" meeting at IRT. + - Date fixed on Oct. 24th +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +### Long term actions +- [-] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - Cancelled +- [-] (1205-6, Eric, Jean) See how to proceed with tool implementation + - Cancelled +- [-] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Cancelled. + +# 2025/09/10 +## Participants + *To be completed* +## Agenda + - Actions + - Code generation from Why3 spec. [Mariem + Salomé] + - Actions about testing? + - Actions to reactivate the WG + - Workshop with Loïc + +## Minutes +- About "failures" (see action 2708-1 below) + - Add in the specification that the implementer have to specify if they have actually done some kind of failure analysis or not. For instance "The conditions of occurrence of failure conditions (overflows, underflows, wraparound, etc.) have not been analyzed." +- Mariem: Presentation of the archive provided by Loïc's that covers the complete process, from formal specification to C code generation for the "where" operator. + - See the [document](../meetings/c_code_generation/sources.md) explaining the content of the archive. + - The "interface" of the Tensor formal specification has slightly changed (use of `List`s instead of `Sequence`s. ). This will require some (slight) modifications of the existing formal specifications. *To be checked*. + - Next step is to use this as an example to implement `concat` and other operators. + +## Actions +- [ ] (1009-1, Eric+Jean) Organize a technical discussion with DNN experts to conclude on the need of broadcasting. + - Among the quesrtions to be discussed: Is broadcasting useful? necessary? is it only a choice of model designers or does it come "naturally" during the export done by frameworks? +- [ ] (1009-2, Eric) Mail to be send on SONNX mailing list for contributions +### New actions +### Past actions +- [ ] (2708-1, Eric) Give short guidelines about error / failure conditions. + - See minutes of 2025/08/27 meeting. The "rules" could be: + > - If the conditions can be expressed on the inputs (e.g., $x \ge 0$ for `sqrt(x)`) , add a condition on the input domain in the specification + > - If the condition cannot be expressed on (or "propagated to") the inputs, express the failure condition at the appropriate level (for instance: + "When computing a matrix multiplication, the result of the accumulation may overflow and the result may "wraparound", leading to an incorrect result." + > - If possible, give a link to the location in the specification where this accumulation is done. + > - Note that some smart implementation may avoid the problem. For instance, when accumulating 2 bits values on a 2 bits accumulator, "3+3-3-3" overflows while "3-3+3-3" does not. + > - So, the relevance of the warning (i.e., "When computing [...]") actually depends on the implementation, but we know that -- in principle -- there might be some cases where an overflow can occur. And this is due to the the very fact that the operation accumulates values. + > - If no indication is given about occurrence of a "failure", this means that the specification is complete and defines what is the expected value for **any** input. + > - In addition, provide "recommendations" about the implementation. A typical example is the one of `SoftMax` where we could recommend the use of the `-max(Xi)` trick. +- [X] (2708-2, Mariem) Put Loïc's contribution in the repo. +- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat` +- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability + - In progress. To be reviewed during next meeting. +- [X] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators? + - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3. + - Conclusion meetings at AI => do a dedicated meeting in SONNX to share the position (see followup 1009-1)) +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification + - Poll link sent during meeting. Possibility to organize a "physical" meeting at IRT. + - Date fixed end of next week. +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) + - Modalities to be discussed +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form to support the analysis + +# 2025/08/27 +## Participants + *To be completed* +## Agenda + - Actions [eric] + - Status of [guidelines](../documents/profile_opset/guidelines.md) and application to [`conv`](../documents/profile_opset/conv/conv.md) and [`concat`](../documents/profile_opset/concat/concat.md) + - Error / failure conditions; Eduardo's remarks on [discussion elements](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments. + - Code generation + - Link with the DeepGreen project (see [slide](./slides/AIDGE.pptx)) + - Events (Mobilit'AI) + - Presentation Alexandre Eichenberger on ONNX-MLIR on 08/10/2025 +## Minutes +- Continuation of the discussion about [Failure modes / error conditions](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments. on the basis of Edoardo's comments: + - It seems that we converge on a baseline where we would simply indicate whether or not the operator may fail (due to a division by zero, wrap-around, etc.) and, if possible, in which conditions such situation may occur. + - If the conditions can be expressed on the inputs, this means that we could possibly add the condition in the specification (a conditions on the input domain) + - If the condition cannot be expressed on (or "propagated to") the inputs, we express the condition at the appropriate level (for instance: + > "When computing a matrix multiplication, the result of the accumulation may overflow and the result may "wraparound", leading to an incorrect result." + - We may give a link to the location in the formula where this accumulation is done. + - Note that some smart implementation may avoid the problem. For instance, when accumulating 2 bits values on a 2 bits accumulator, "3+3-3-3" overflows while "3-3+3-3" does not. + - So, the relevance of the warning (i.e., "When computing [...]") actually depends on the implementation, but we know that -- in principle -- there might be some cases where an overflow can occur. And this is due to the the very fact that the operation accumulates values. + - If no indication is given about occurrence of a "failure", this means that the specification is complete and defines what is the expected value for any input. + - In addition, we will also provide "recommendations" about the implementation. A typical example is the one of `SoftMax` where we could recommend the use of the `-max(Xi)` trick. +- Concerning code generation from the Why3 spec.: Loïc has provided us with an example. We are currently analyzing it and will try to apply it on `conv` and `concat`. +## Actions +### New actions +- [X] (2708-1, Eric) Give short guidelines about error / failure conditions. + - See minutes of 2025/08/27 meeting. +- [ ] (2708-2, Mariem) Put Loïc's contribution in the repo. +- [ ] (2708-3, Mariem, Salomé) Try to apply Loïc's approach to `conv` and `concat` +### Past actions +- [X] (3007-1, Eric, All) Collect ideas exchanged on Error Conditions during the meeting. To be discussed during next meeting. + - Document is [here](../meetings/errror%20conditions/2025-07-30%20-%20Discussions.md) with Eduardo's comments. +- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability +- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators? + - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3. +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) + - A prototype tool is currently being developed. Possibly available in October (this is **not** a commitment). +- [-] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. + - Cancelled +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form to support the analysis + + +# 2025/07/16 +## Participants + *To be completed* +## Agenda + - Actions [eric] + - A few words about the idea of a core set of operators + - See note [here](./core_ops/core_ops.md) [eric] + - Guidelines + - See [here](../documents/profile_opset/guidelines.md). + - Status of MLIR presentation by Alexandre. (2025/10/08) [eric] + - Status of work on formal spec [Mariem and Salomé] + - Presentation of work on the formalization of Scalar and the first results about C code generation. + - Ideas on error condition spec [Franck] + - Status of discussions about tests (Andreas, Justin, Christian, Eric, Jean) [eric+jean] + - See [minutes](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/testing/2025-07-29-tests.md) +## Minutes + - See above. + - See Franck slides [here](./errror%20conditions/2025-07-30-SONNX_error.pdf) + - See elements discussed about the errors conditions [here](./errror%20conditions/2025-07-30%20-%20Discussions.md) +## Actions +### New actions +- [ ] (3007-1, Eric, All) Collect ideas exchanged on Error Conditions during the meeting. To be discussed during next meeting. +### Past actions +- [ ] (1607-1, Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 traceability +- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators? + - Introduce a specific "broadcast" operator to make the operation explicit in the spec. See 1607-3. +- [X] (1607-3, Dumitru) Write a few lines to explain the "mixed approach" to handle broadcasting. + - Mail exchange collected in [here](./broadcasting/dumitru-2025-07-21.md) +- [X] (1607-4, Franck) Write a few lines to explain the approach to handle errors: ask implementers to provide error conditions + - Description sent on 2025/07/16 +- [X] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines... + - First proposal in the [guidelines](../documents/profile_opset/guidelines.md). To be discussed (see Franck's pres.) +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form to support the analysis + + + +# 2025/07/16 +## Participants + Salomé, Jean, Tomé, Cong, Jean-Baptiste, Franck, Eric, Dumitru, ... +## Agenda + - Actions + - A few words about shape inference and broadcasting + - A few words about the "minimal corpus of operators" + - A few words about runtime errors + - Feedback on presentation to WG114 (see [slides](./Other_meetings/SONNX%20-%20WG114.pdf)) + - ED324 = ARP 6983 + - Feedback on meeting with DeepGreen + - Meeting with ONNX infra about testing + - Opportunities for presentation of our work: + - Mobilit'AI. + - Stragegy for next period +## Minutes + - A few words about shape inference and broadcasting + - Forbidding broadcasting may reveal extremely penalizing because making it "explicit" boils down to creating actual tensors whereas broadcasting is essentially a manipulation of indexes done at operation level. + - We have to check if this restriction is necessary, sensible, and applicable. + - See actions (1607-2) and (1607-3) + - A few words about the "minimal corpus of operators" + - Some operators can be described on the basis of simpler, "atomic" operators. For instance, a `softmax` can be described as the composition of `Exp` and `ReduceSum`. It may also be described as $s(z_i) = {e^{z_i} \over \sum_{j=1}^K e^{z_j}}$. A `Relu` can also be described as a combination of operators, etc. + - Do we want to apply this modular approach? + - What would be the "minimal corpus of operators"? + - Could we have a "non-modular" informal specification (that describes the operation using a mathematical formula) and a modular formal specification? + - A few words about runtime errors + - See this [document](./errror%20conditions/error_conditions_2.md) + - The question is to define a strategy to specify the error condition that may occur during the execution of an operator. + - In the FP domain, we agree that no exception is considered but that we use the IEEE special values Inf, NaN to propagate the errors up to the operator's output. + - We have to do a systematic analysis of the error conditions and indicate what are the possible error conditions (e.g., in the case of the SoftMax : overflow). The specification may still indicate that no error shall occur (i.e., no NaNs) because we know (as it is the case for SoftMax) that there is a means to avoid it (e.g., in the case of SoftMax : substract the max value). This means that all implementations will have to apply this means (or something equivalent). Otherwise, the specificaton of the operator shall indicate in the "Error conditions" section what errors can happen (e.g., an overflow) with a NaN as a result. + - Franck proposes an approach in which the implmeter would provide the description of error behaviour (see 1607-4) + - For integer operations, we will provide the **exact spec** of the operations. For signed ops, the specification will show the 2's complement description of the operation, see [here](./errror%20conditions/error_conditions_2.md)) + - Feedback on presentation to WG114 (see [slides](./Other_meetings/SONNX%20-%20WG114.pdf)) + - Presentation was appreciated. + - One slide or two on ED 324 <=> SONNX traceability must be done (see 1607-1) + - Feedback on meeting with DeepGreen + - SONNX will be used to specify (and partically provide code) for a C-code backend + - Meeting with ONNX infra about testing + - Meeting planned on July 29th with people from the infra WG (Andreas, Justin, Christian). + - Opportunities for presentation of our work: + - Mobilit'AI. + - We will probably present a poster à that occasion. + - Stragegy for next period + - For the informal specification + - Phase 1: Clean-up and ensure consistency between the guidelines and the 3 cononical examples (CONV2D, CONCAT, and aanother simple operator) + - Phase 2: mailing to the potential contributor to ask for condibution on the basis of the guidelines and existing examples + - Phase 3: meeting to present the approach and share work + - The same approach shall be followed for the formal specification. + - Presentation of the `Add` operator by Salomé. +## Actions +### New actions +- [ ] (1607-1) Jean-Baptiste, Sergei (?)) Produce a synthesis of SONNX <=> ED 324 tracaibility +- [ ] (1607-2, Eric, Jean) Check what is the actual need in terms of broadcasting (ask users, checks models, check operators providing this capability). What would be the effort to integrate broadcasting in the specification of our operators? +- [ ] (1607-3, Dumitru) Write a few lines to explain the "mixed approach" to handle broadcasting. +- [ ] (1607-4, Franck) Write a few lines to explain the approach to handle errors: ask implementers to provide error conditions +- [X] (1607-6, Tomé) Provide the description of the 2 interships. + - Description sent on 2025/07/16 +### Past actions +- [ ] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines... +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) +- [X] (1806-6, Eric) Initiate the specification of matrix multiplication +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form to support the analysis + +# 2025/07/02 +## Participants +- Salomé, Alexandre, Jean, Sebastian, Cong, Mariem, Jean-Baptiste, Eric,... +## Agenda +- Status of actions +- Misc news: + - Feedback on June 20th workshop on formal specification and verification (Eric, Mariem, Jean) + - Structure of repo for Why3 formal specifications (Mariem) - A question about model readability (Eric) + - Slides for WG114 (see [here](./Other_meetings/SONNX%20-%20WG114.pdf) (draft), Eric) + - Issues [see [here](https://github.com/onnx/onnx/issues/3651)] + - Back to action 0412-6... + - Two questions + - What shall we "say" about inner overflows, division par zeros, etc.? + - *Do we need the model to be human-readable?* + - For information: SIONNX (see [here](https://github.com/alibaba/sionnx)) + - Description of the algorithm (python). Example for [conv](https://github.com/alibaba/sionnx/blob/master/include/conv.algorithm) + - Description of the signature. Example for [cov](https://github.com/alibaba/sionnx/blob/master/include/onnx_conv.td) +## Minutes +- Long discussion about overflows etc. See action (0207-1). +## Actions +### New actions +- [ ] (0207-1, Eric) Do a "synthesis" of the discussion about overflows, etc., discuss with the WG, find a consensus, add to the guidelines... +### Past actions +- [X] (1806-1, Eric) Provide a complete (simple) spec example for 1 op that can be reproduced on the other ops... + - See [matmul](../documents/profile_opset/matmul/matmul.md) +- [X] (1806-2, Mariem) Provide Franck with the C code of the conv2d operator. + - Done. The code is [here](./attachments/conv2d.c). +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) +- [ ] (1806-6, Eric) Initiate the specification of matrix multiplication +- [X] (1806-7, Jean-Baptiste) Provide Eric with ARP/SONNX analysis material +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). + - Done on the [matmul](../documents/profile_opset/matmul/matmul.md) +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form to support the analysis + + +# 2025/06/18 +## Participants +- Eric, Mariem, Salomé, Alex, Henri, Dumitru, Jean-Baptiste, Mohammed, Franck, Jean-Loup, Jean +## Agenda +- Status of actions. +- Misc news: + - Presentation to ONNX meetup (["video"](./general/2025-06-09%20-%20MEET-UP/SONNX%20-%20Meetup%202025.7z)) + - Review and update of ops... + - The existing specifications must be updated to comply with the [specification guidelines](../documents/profile_opset/guidelines.md). A first pass has been done on [`abs`](../documents/profile_opset/abs/abs.md). + - Request for participation to work on the graph execution + - Jean and Jean-Loup are OK to help. Eric to organize a working session. (See action 1806-4) + - Eric to present SONNX to the WG114. One slide about the ARP/SONNX mapping could be useful. (see action 1806-7) +- Numerical accuracy + - Franck is preparing some elements to be put in the guidelines. + - Franck is also working on the analysis of the matrix multiplication. It could be wise to specify this operator in order to have a complete example including numerical accuracy analysis (see action 1806-6). + - He needs some C code for the conv2D. (see action 1806-2) + - Numerical analysis verification will be computed by executing the C++ code. The C code of the operators (generated using Why3) will be integrated as is. The tool leverage's C++ operator overloading capability. +- The number of participants to the bi-weekly meetings is decreasing steadily... + - Give a "good" example of spec and invite people in the mailing list to contribute... + - Organize new presentations (see action 1806-3) +- Discussion about June 20th second workshop on formal methods (off-main meeting). + - This workshop concerns those that have been involved in the first workshop (other may join, please contact me). It will take place at IRT. A link will be provided. + - On the basis of what has been done on conv2D, concat, and graph, clarify/complete the method, obtain guidelines to carry out proofs, obtain material to support self-training, obtain guidelines to generate C code, etc. + +## Actions +### New actions +- [ ] (1806-1, Eric) Provide a complete (simple) spec example for 1 op that can be reproduced on the other ops... +- [X] (1806-2, Mariem) Provide Franck with the C code of the conv2d operator. + - Done. The code is [here](./attachments/conv2d.c). +- [ ] (1806-3, Eric, Dumitru) Organize a presentation of Dumitru's approach to handle RNNs. (please complete [this document](./presentation_proposals.md)) +- [ ] (1806-4, Eric) Organize a "physical" working session on the graph specification +- [ ] (1806-5, Eric, Jean) Resend a "call for participation" to the mailing list (at least once we have a good template spec) +- [ ] (1806-6, Eric) Initiate the specification of matrix multiplication +- [ ] (1806-7, Jean-Baptiste) Provide Eric with ARP/SONNX analysis material + +### Past actions +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. + - First trial on something simpler than the conv (matrix multiplication). +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +### Long term actions +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form + +# 2025/06/04 +## Participants +- Nicolas, Salomé, Sebastian, Henri, Jean-Loup, Jean, Eric, Jean-Baptiste, Frédéric, Eduardo,... +## Agenda +- Status of actions. +- Misc news: + - ONNX meetup on June 9th: a ["video"](./general/2025-06-09%20-%20MEET-UP/SONNX%20-%20Meetup%202025.7z) has been prepared. + - The abstract of our [paper on SONNX](../documents/publications/ERTS2026/2025-06-02%20SONNX%20-%20ERTS%202026%20abstract%20-%20final.pdf) has been submitted to [ERTS 2026](https://conference-erts.org/). + - Salomé has updated the informal and formal specification of the ``concat`` operator. Will be pushed soon. + - Frédéric has presented his first attempt to specify errors for operators using floating point values. + - As a proof of concept, it has been applied to the [``abs``](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/documents/profile_opset/add/abs.md) and [``add``](https://github.com/ericjenn/working-groups/blob/spec-with-numerical-accuracy-info/safety-related-profile/documents/profile_opset/add/add.md) operators. + - The specification gives the properties that an implementation shall satisfy considering the errors due to the floating point arithmetic. Methods errors are not considered. The properties are "conservative" in the sense that they consider any floating point values. Tighter bounds could be obtained for smaller domains. + - A C++ implementation to compute the errors is provided (not fully implemented for the moment...). + - This implementation evaluates (will eventually evaluate) the error **symbolically**. + - Verification of the assertion will also be done symbolically. + - The next step would be to address the ``conv`` operator. +## Actions +### New actions +- [ ] (0406-1, Franck) Specify numerical accuracy for the `conv` operator. +### Past actions +- [X] (2105-1, Salomé) Provide answers (OK,KO, TBdiscussed) to [Eric's comments](../documents/profile_opset/concat/reviews/eric.md) for the `concat` operator. +- [X] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [X] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. + - Will be done during next ONNX team up +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form + +# 2025/05/21 +## Participants +- At least: Salomé, Sabastian, Dumitru, Sergei, Jean, Eric, Mariem, Mohammed, Nicolas, ... +## Agenda +- Status of actions. +- Misc news: + - next workshop on formal specification + proof... + - discussion with WG114 and presentation to WG114 +- Presentation of the `concat` operator by Salomé +- Brief presentation of the informal and formal specification of a `graph` by Eric + - Review of the `graph` informal specification by Mohammed. +- Overview of the [Specification guidelines](../documents/profile_opset/guidelines.md) +- Status of operator specification +## Actions +### New actions +- [ ] (2105-1, Salomé) Provide answers (OK,KO, TBdiscussed) to [Eric's comments](../documents/profile_opset/concat/reviews/eric.md) for the `concat` operator. +- [X] (2105-2, Mohammed) Upload graph review form +### Past actions +- [X] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R. + - Done on May 13th. +- [X] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.* + - Done and to be presented during the meeting. +- [X] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md) + - [Review by Edoardo](../documents/profile_graph/reviews/edoardo.md) +- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX +- [X] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator. + - Work in progress. Fist review by Eric. + - Done, to be presented during the meeting +- [X] (0904-4, Joao) Investigate internship to support SONNX +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +- [X] (2603-3, all) Think about our expectation concerning numerical precision (add req) + - See minutes of [meeting with Franck ](../meetings/numerical%20accuracy/2025-04-11_Meeting_with_Franck.md) +- [C] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [X] Tag created. + - [C] Determine who can apply this tag? + - Cancelled. +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. + - Will be done during next ONNX team up +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form + +# 2025/05/07 +## Participants +TBC +## Agenda +- Presentation Alex: "[About quantization and ONNX in Airbus' context"](./slides/2025-05-07-Alex-DIGONNET-SONNX%20quantization%20representation%20formats.pdf)" +- Certification referential (RMT) by Jean-Baptiste +- Presentation of SONNX to ALTERA and ANSYS +## Actions +### New actions +No new action. +### Past actions +- [ ] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R. +- [ ] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.* +- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md) + - [Review by Edoardo](../documents/profile_graph/reviews/edoardo.md) +- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting) +- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator. + - Work in progress. Fist review by Eric. +- [X] (0904-4, Joao) Investigate internship to support SONNX +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req) + - See minutes of meeting with Franck: +- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [X] Tag created. + - [ ] Determine who can apply this tag? +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. + - Will be done during next ONNX team up +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [X] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator + - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md) +- [C] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops... + - Cancelled +- [C] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... + - Cancelled +- [C] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") + - Cancelled +- [] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form + +# 2025/04/23 +## Participants +TBC +## Agenda +- Feedback on meeting with Franck (CEA) about error estimation. +- ERTS 2026 paper (see [here](https://share-is.pf.irt-saintexupery.com/s/ipMLHmEZ8adgBDY), read access) +## Minutes +- Review of actions +- Feedback on meeting with Franck. +- Welcome to Christophe Ratajczak from [EM Microelectronics](https://www.emmicroelectronic.com/welcome) +- Salomé has made some significant progress on the informal and formal specification of the [``concat``](https://onnx.ai/onnx/operators/onnx__Concat.html) operator. Work to be pushed to the repo by the end of the week. Presentation to be done in a future meeting. +## Actions +### New actions +- [ ] (2304-1, Eric+Jean) Plan presentation of SONNX to Christophe R. +- [ ] (2304-2, Mohamed) Review of the informal spec of the [graph semantics](../documents/profile_graph/graph.md). *Please place the review in the "review" directory.* +### Past actions +- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md) +- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting) +- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator. +- [ ] (0904-4, Joao) Investigate internship to support SONNX +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +- [X] (0904-6, Eric) Clarify the concept and req of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) in our context. + > Numerical stability in numerical analysis refers to how errors are propagated by an algorithm during computation. These errors can come from several sources, like round-off errors due to finite precision (e.g., floating-point arithmetic), truncation errors from approximating infinite processes (like Taylor series), input errors (e.g., measurement uncertainty). A numerically stable algorithm is one in which small changes in input or small intermediate errors do not grow significantly and affect the final output too much. In contrast, an unstable algorithm may amplify these small errors, leading to wildly incorrect results. See [this page](./numerical%20accuracy/numerical_stability.md).\ + **=> The algorithm given in the profile specifies the result. It does not specify how to *compute* the result. So the algorithm may be naïve and unstable. Thi sis true for the specification in $\mathbb{R}$.\ + => What about specifications in other domains (floats,integers)?** +- [ ] (2603-3, all) Think about our expectations concerning numerical precision (add req) + - See minutes of meeting with Franck: +- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [X] Tag created. + - [ ] Determine who can apply this tag? +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator + - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md) +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops... +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form +# 2025/04/09 +## Participants +- Eric, Jean, Salomé, Sergei, Dumitru, Cong, Andreas, Sebastian, Mohammed, Jean-Baptiste, Joao, Edoardo, Henri, Jean-Loup, Alexandre +## Agenda +- Presentation of ONNX MLIR by Alexandre Eichenberger +- Discussion about the "hierarchical/modular" way to specify operator, use of `onnxscript` (with Nicolas and Dumitru) +- Status of reviews + - LSTM + - Graph + - Requirements +- Feedback on discussions with WG 114 and what to do next. +## Minutes +- Newcomers. +- Alexandre was not able to do the presentation today. To be (re-)rescheduled... +- Very long discussion on the specification of operators... + - Our objective is (i) to keep the informal specification as simple and readable as possible and (ii) to avoid multiplying formalisms. + - To make a long story short, and since we have already decided to use Why3 as our formal language... we have concluded that + - the informal specification will continue using a simple, informal, possibly mathematical representation (see e.g., [``lstm``](../documents/profile_opset/lstm/lstm.md)), + - other representations may be provided as "examples" + - the formal specification (in Why3) must adopt some "modular" hierarchical approach where, when applicable, an operator shall be specified using some "primitive" operators (e.g., ``lstm`` is specified using ``scan``). + - Review of the informal [graph spec](../documents/profile_graph/graph.md): done by Jean-Loup (thanks!). To be completed to cover e.g., ``loop`` operator. To be reviewed again by Edoardo and Sebastian (action 0904-1) + - [Requirements](../deliverables/reqs/reqs.md): reviewed by Edorardo (thanks). Comments have been taken into account and discussed with Edoardo. + - Discussions about requirements about + - "determinism of resource usage and execution times" + - Remove the req about memory + - Concerning execution times, there is at least the ``loop`` operator that can (possibly) raise some problem: we have to ensure that the number of iterations is bounded. Dumitru will checks if there are other operators whose execution time or resource may not be bounded statically. (See action 0904-5) + - "traceability to training model and environment" + - To be removed because (i) it'll be too complicated if we were to specify the exact training environment, (ii) traceability can be ensured by conf mngt, etc. We simply have to give the capability to embed meta-data in the model (there is already a req about that). + - "numerical stability" + - We were not able to remember where this req comes from. The concept of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) was discussed and various definitions/interpretations were given, none of them being really convincing. The main questions are: (i) what do we actually require? and (ii) why do we require it? (See action 0904-6 ) +- Feedback to WG 114 + - The main point concerned the concepts "exact" and "approximate" replications. After many discussions, we agreed to remove these concepts from the core of the document and put it in an Annex. Those concepts where not at the "same level": "exact" replication expresses a relation between output or intermediate tensor values while "approximate" replication expresses a relation between performances. FurthermoreIn addition, the practical usage of these concepts was not that clear, especially for the "approximate replication" that basically state that the implemented model must comply with its spec... + - We have to complete the analysis of the ARP+concept paper in order to ensure that we are not missing reqs. See action 0904-2. +- Shouldn't we do the same type of analysis for other domains (e.g., ISO 8800 in the automotive domain)... +- Formal specification + - We shall start from [Loïc's proposal](../meetings/formal_methods/code/) in which he specifies the ``where`` operator. Salomé will do the specification work on ``concat`` (action 0904-3). This will need some additional work due to the use of lists of tensors as inputs. +- Other contributions + - Joao is investigating starting an internship to support SONNX activities by the end of Q2. +## Actions +### New actions +- [ ] (0904-1, Sebastian, Edoardo) Review the [graph spec](../documents/profile_graph/graph.md) +- [ ] (0904-2, Jean-Baptiste) Complete the analysis of the ARP+Concept papers to collect potential reqs for SONNX (to be done for next meeting) +- [ ] (0904-3, Salomé) Specification (informal and formal) of the ``concat`` operator. +- [ ] (0904-4, Joao) Investigate intership to support SONNX +- [ ] (0904-5, Dumitru) Scrutinize the set of ONNX ops to see if there are other operator causing similar concerns as ``loop``. +- [ ] (0904-6, Eric) Clarify the concept and req of [numerical stability](https://en.wikipedia.org/wiki/Numerical_stability) in our context. + - > > (From Wikipedia: The usual definition of numerical stability uses a more general concept, called mixed stability, which combines the forward error and the backward error. An algorithm is stable in this sense if it solves a nearby problem approximately, i.e., if there exists a Δx such that both Δx is small and f (x + Δx) − y* is small. Hence, a backward stable algorithm is always stable. +### Past actions +- [X] (2603-1, Eric, Nicolas,Jean-Loup) Analysis of all remarks about operator [lstm](../documents/profile_opset/lstm/lstm.md) +- [X] (2603-2, Eric, Edoardo) Review of the [requirements](../deliverables/reqs/reqs.md) in order to produce a clean version (possibly incomplete). + - Done on 2025/04/08 +- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req) +- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [X] Tag created. + - [ ] Determine who can apply this tag? +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [X] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN. + - First version presented during the 26/03 meeting. + - To be completed for next meeting (Dumitru and Nicolas) + - Discussed during the 2025/04/09 meeting. +- [X] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator). +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [ ] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator + - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md) +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops... +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... +- [X] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) + - Cancelled. +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form +# 2025/03/26 +## Participants +- E. Jenn, J. Souyris, H. Belfy, M. Turki, J.L. Farges, E. Manino, D. Potop Butucaru, M. Belcaid, N. Valot et Duy Khoi Vo. +## Agenda + - Operators + - [Current list](../documents/profile_opset/): abs, add, constant, conv, div, gemm, less, log, lstm, matmul, mul, net, pow, sigmoid, sqrt, sub, tanh, where + - We have to have at least 1 reference example (considered consensually "perfect"). + - Then we have to write / review the other operators against it. Which operator? + - [LSTM operator](../documents/profile_opset/lstm/lstm.md). [Review by Jean-Loup](../documents/profile_opset/lstm/reviews/jean-loup.md) + - [Graph semantics](../documents/profile_graph/graph.md) + - Feedback on [2025/03/18 workshop on formal specification and verification](./formal_methods/minutes.md) + - Review and finalization of the [specification](../deliverables/reqs/reqs.md). Who want's to join? + - Status on ARP + - Discussion on March 14th : + > 1. The exact replication is the only option to preserve the properties demonstrated during the design phase. It means the “bit accurate” replication of the semantics of the designed model (expressed by the MLMD) in the target environment. + > 2. The approximative replication means that the properties (performance, generalization, stability and robustness) of the designed model (expressed by the MLMD) are re-assessed in the target environment and that the results are within an acceptable epsilon from the results obtained in the design environment. The epsilon should be specified by the applicant in the MLC requirements. + - Review of ONNX IR. Who? + - Work on formal spec. + - Traceability to certification constraints + - *What do we do next?* + - Mail to the troop? [here](../documents/Attic/call.md) + +## Minutes + - Discussion about the need to have a few set of "informal specifications" to be used as references for the writing and the review of the operators. + - As of today, none of the operators are completely satisfying. "conv", "where" and "lstm" are good candidate for they cover different aspects, issues. We may add "div" for it handles INF and NaNs and possible "sigmoid" since it raises problems wrt overflow / underflow (see below) + - Presentation of the latest version of the [LSTM operator](../documents/profile_opset/lstm/lstm.md) by Nicolas and discussion of [Jean-Loup's review](../documents/profile_opset/lstm/reviews/jean-loup.md). + - Several comments fro Jean-Loup were discussed. A specific meeting with Nicolas, Jean-Loup and Eric has to be organized to complete the review process. + - Dumitru proposes to build the informal specification of "lstm" on more primitive operators (e.g., "scan"). A first version was presented that defines "lstm" in a hierarchical way using more primitive operators. This first version will be completed by Dumitru (see action (1203-2, Dumitru)) and we will take a decision about the most appropriate way afterwards. + - Presentation of the [sigmoid operator](../documents/profile_opset/sigmoid/sigmoid.md) by Nicolas. + - For the float version of the operator, the specification proposes an "algorithm" that discriminates two cases: $X \gt 0$ and $X \leq 0$. This discrimination is aimed at providing the best output (prevent over/under flow) for the largest input domain. + - In some sense, it could be considered as a "guideline", a "recommendations". We need at least to give the very reason for discriminating the two cases (some hints are given in the spec). + - Note that this design may also be justified with respect to the symmetry of the function. + - Another possibility could be to define the `sigmoid` using `tanh` that is a standard IEEE 754 operator ($\sigma(x)=​{1+tanh(2x​)\over 2}$). + - This discussion raises, again, our problem with the specification of numerical properties... (For the record, see [this document](../meetings/numerical%20accuracy/01_what_is_the_issue.pdf) presented in a previous meeting) +## Actions +### New actions +- [ ] (2603-1, Eric, Nicolas,Jean-Loup) Analysis of all remarks about operator [lstm](../documents/profile_opset/lstm/lstm.md) +- [ ] (2603-2, Eric, Edoardo) Review of the [requirements](../deliverables/reqs/reqs.md) in order to produce a clean version (possibly incomplete). +- [ ] (2603-3, all) Think about our expectation concerning numerical precision (add req) +### Past actions +- [ ] (2003-1, Andreas) Create a "sonnx" label and a group with the appropriate rights to tag issues. (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [X] Tag created. + - [ ] Determine who can apply this tag? +- [X] (2003-2, Eric) Update SONNX landing page to point to interesting material... (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - Added a "contents" section in the [SONNX main page](../README.md). (Not yet pulled to the main branch) +- [ ] (2003-3, Eric) Initiate discussion in WG about ONNX integration and propose possible solutions to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) +- [X] (2003-4, Eric) Give an example of the two categories of restrictions (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - See proposal for the [`conv` operator](../documents/profile_opset/conv/conv.md) +- [X] (1203-1, Eric) Propose a first specification of the graph execution semantics on the basis of Dumitru's slide and ONNX doc. + - See [here](../documents/profile_graph/graph.md) +- [ ] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN. + - First version presented during the 26/03 meeting. + - To be completed for next meeting (Dumitru and Nicolas) +- [X] (1203-3, Jean-loup) Do a review of the LSTM operator + - Review is [here](../documents/profile_opset/lstm/reviews/jean-loup.md) +- [ ] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator). +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +- [X] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work... + - Moved to action (2003-3) +- [ ] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator + - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md) +- [X] (1202-5, All) Define appropriate rules to handle multiples types without multiplying the specifications. + - See example of operator [`Div`](../documents/profile_opset/div/div.md)) +- [cancelled)] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM. +- [X] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th. +- [X] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`. +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops... +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... +- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) +- [X] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.) + - Moved to the general activity on formal spec. +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + - Take into account the new modality to manage and report issues to ONNX (from [2023/03/19 meeting](./Other_meetings/2025-03-20-An-Er-Se-Je.md)) + - [ ] Create a review form + +# 2025/03/12 +## Participants + - Nicolas, Cong, Dumitru, Anne-Sophie, Eric, Jean-Baptiste, Henri, Jean-Loup, Andreas (partially) (at least). + - As usual, the meeting has been recorded. See [here] +## Agenda + - Problem with meeting invitations (?) + - Review of actions + - Presentation of ONNX graph semantics (Dumitru) + - Status on ARP + - First set of answers received. Will be completed by Friday 14th + - Review of newly described operators (see [DIV](../documents/profile_opset/div/div.md), [LSTM](../documents/profile_opset/lstm/lstm.md), other?) + - Subjects raised by Andreas and Sebastian and further discussed with Nicolas (see [minutes](../documents/profile_opset/lstm/reviews/meeting_mom.md)): + - Relations with ONNX + - Compliance with ONNX spec + - Nicolas: + > I propose that for each operator .md file, we specify the full compliance with ONNX spec and we add a bottom section SONNX in the operator page, where we express the restrictions like : this input XXX shall be static, the B bias shall be explicitly defined, Broadcast not supported (reshape shall be prepended when required)... + > This will show our specification work (math expressions, illustrations) mainstream to all the ONNX community from the ONNX documentation entry point, and also the SONNX 'value' for curious/interested people at the bottom of each operator page. + - Visibility + - Sebastian/Nicolas: + > For visibility and adoption, we shall avoid having a SONNX documentation entry point different from mainstream ONNX. + - Andreas + > 1. One if not the central meeting at Onnx is the SIG Operators meeting. It takes place every month. + > I think we should actively bring our points there to\ + > a. Get a discussion about our thoughts, from the experts who know operators, know how the way would be to implement them.\ + > b) We could also draw more attention to our initiative there. + > 2. The next Onnx Community Meetup will probably take place in June 2025. (https://github.com/onnx/steering-committee/blob/main/meeting-notes/2025/20250305.md)\ + There we will have the opportunity to present what we are doing and what we have achieved. + > 3. I think we need to create a lot more issues at https://github.com/onnx/onnx in order to work out the concerns more clearly,or to be able to discuss them even more with the community there, or to be able to refer to them in the Operators Meeting. + > 4) As there are certainly already issues concerning SONNX at the moment, I could well imagine a new label “sonnx” for this, so that we can filter even better according to the topics. => you could ping me "andife" directly at the github issue and I add that specific label + - Tooling + - ONNX to SONNX converter + - Model checker + - *How to fund those developments?* + - Other + - Graph semantics... Who? +## Minutes +- [Presentation of the ONNX graph semantics by Dumitru](./slides/2025-03-12-Dumitru%20on%20ONNX-graph-semantics.pdf) + - The semantics is pretty simple (as far as I [eric] understand + - considering a Directed Graph composed of nodes and edges, where + - a node is an operator + - a edge connects a node output to a node input + - the graph is acyclic + - considering that an edge carries either no value of a value(ii) the graph contains no cycle, + - considering that all edges carry no value initially + - considering that an edge gets a value when the node whose output is associated with is executed + - considering that a node can ony be executed when all edges associated with its inputs have a value + - executing a graph means executing every node until all nodes have been executed. + - The graph is stateless, so if the computation requires a state, this state shall be managed outside of the graph. Such question are (very) relevant when dealing with reactive systems, but they do not concern the SONNX spec. + - Pipelining or other implementation concerns (e.g., scheduling of nodes execution) are not relevant to the specification + - Questions were raised about + - operators implementing a random behaviour + - a deterministic behavior can be achieved by providing seeds to each and every random op. + - batch norm + - batch norm is replaced by a constant at inference time + - Brief presentation of the DIV operator that shows of implementation of the same operator for Real, Float and Int number can be specified. + - Presentation of the LSTM operator + - A few verifications and corrections needs to be done, see action [1203-4]. + - Discussion about the presentation of our work to ONNX + - Eric: I would prefer to present our results once we are completely happy with a first subset of operators. This will hopefully be the case in for the Meetup in June. In the meantime, we have to figure out with ONNX people who our work needs to be "integrated" with theirs. See action [1203-5] + - Discussion about the tooling + - We have identified two tools: one to check a model with respect to SONNX, and one to convert a model from standard ONNX to SONNX. + - With respect to certification, the second one may not be a "good idea" . see acton [1205-6]. + - The last point about "issues" has not been discussed. +## New actions +- [ ] (1203-1, Eric) Propose a first specification of the graph execution semantics on the basis of Dumitru's slied and ONNX doc. +- [ ] (1203-2, Dumitru) Propose an draft spec of LSTM where the operator would be specified using SCAN. +- [ ] (1203-3, Jean-loup) Do a review of the LSTM operator +- [ ] (1203-4, Nicolas) The relation between the directions and the dimension of the tensors shall be expressed by a constraint, not the assignment of a variable. The attributes must be presented before the description of the operator. Check that any activation function can be used for atc1 to act3. For the backward LSTM, check if the output needs to be reverted. Create a jupyter note (in collab) to illustrate the use of the operator (in the same way as for the DIV operator). +- [ ] (1203-5, Eric, Jean and Andreas) Organize a meeting with ONNX to present our first results (in order for them to have an idea of the expected end-result) and discuss what could be the integration modalities. +- [ ] (1205-6, Eric, Jean) See how to proceed with tool implementation +## Past actions +- [X] (1202-1, Eric) Reschedule Alexandre presentation + - Presentation planned on April 9th. +- [ ] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work... +- [ ] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator + - [X] Eric: review and modification of operator [`Div`](../documents/profile_opset/div/div.md) +- [ ] (1202-4, All) Define the appropriate way to specify the behaviour of operators for value out of range. Apply the approach on the `div` operator, for parameters in $\mathcal R$ and, `double`and `int`. + - [X] Eric: See operator [`Div`](../documents/profile_opset/div/div.md) +- [ ] (1202-5, All) Define appropriate rules to handle multiples types without multiplying the specifications. + - See example of operator [`Div`](../documents/profile_opset/div/div.md)) +- [X] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level. + - Only tensors of class "Tensors" are supported (SparseTensor are not supported). Such restriction applies to all operators. They are placed in document [General restrictions](../documents/profile_opset/general_restrictions.md) +- [X] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answers given by Sebastian. +- [X] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)). + - Done for operator `Div`. +- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM. +- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th. +- [X] (2901-6, Edoardo) Check how to involve students in the specification work. + - On 12/02 : Edoardo has done some internal advertisement... waiting... +- [X] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper. +- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`. +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops. +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... + - *Thinking...* +- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) +- [X] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me. +- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.) + - Discussion on-going with Loïc on the formal specification strategy... + - Meeting planned to reach a final consensus... + - Meeting done. See [minutes](../meetings/formal_methods/minutes.md). +- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx) +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + +# 2025/02/26 +*Canceled.* + +# 2025/02/12 +## Agenda +- Presentation of ONNX MLIR (Alexandre Eichenberger) [postponed] +- Review of actions +- Some questions from Sebastian + > I'm still not 100% clear, what we want to achieve with our operator specifications. + + > **Is it intended that they appear in the official ONNX documentation and then the restrictions for SONNX may be below in the same text?** + > Otherwise, they will probably not be very visible to the public... See action (1202-0) + + > And is it clear that the norms you have to fulfill will require these formal specifications? + + > We work in very safety-critical projects and so far, it seems that a formal verification of numerical correctness compared to the original trained model is enough for most customers on our side (...] + + > For me the most critical operators are those that can create overflow or division by zero and there are a lot of them: Div, Exp, Log, Pow, Softmax, SoftPlus, Sqrt See action (1202-3) +- "Review" of Henri's work +- Review of Jean-Baptiste work ( See action (1202-2) ) +- Reply from the WG114 on our questions + +## Attendees +(???) + +## Minutes +- Presentation of ONNX MLIR (Alexandre Eichenberger) to be rescheduled. ( See action (1202-1) ) +- "Review" of Henri's work + - See action (1202-3) + - See new operators [here](../documents/profile_opset/) + - Question about error conditions: *How do we specify as the expected behaviour of an operator when a parameter is out of range (e.g., denominator of `div` is 0?)* + - In the `div`spec, for instance, Henri has proposed to return `inf` (when the numerator is different from zero, otherwise a NaN shall be returned). + - We have proposed a [first set of rules](./errors/error_specification.md). Basically, the behaviour of the operator is not defined when we cannot guarantee that its parameter are in range. However: + - This raises a problem because more often than not, we cannot guarantee that the error condition (e.g., $x<=0$ for operator `log`) will not occur. + - This would mean that any output value would be suspicious while we could actually be able to handle appropriately some singular values (such as /0)... + - *This is clearly not satisfying.* We have to propose a better way to handle these cases. We may get some inspiration from the C standard: our interpretation would be determined according to the semantics of ISO C. + - Note that we have to discriminate cases where the operation is mathematically undefined (e.g. /0) from cases where the implementation of the operator may go wrong (e.g., overflow). Considering the `div` operator, if we are in $\mathcal R$ >, there is no `inf` value to be returned: the operation is simply undefined. + - See action (1202-4) + - Question about types + - The first batch of operators are specified in $\mathcal R$, so there should be no reference to types. In a second phase, we will have to specify the operators with actual types. When an operator has multiple parameters, we should either require all parameters to have a specific type, or the same types, etc. This may lead to many specifications... + - See action (1202-5) +- Discussion about the integration of our work in ONNX (see Sebastian's question above). + - Our work must be part of the official ONNX documentation + - Our spec could be added to the existing doc as a link to "SONNX". + - A process shall be defined in order to ensure the consistent evolution of ONNX and SONNX. + - We propose to wait until we have fully covered a few operators (incl. with their implementation types) + - See action (1202-6) +- Presentation of Jean-Baptiste's work + - See action (1202-2) + - This work makes a mapping between the EASA's concept paper and the ARP. it also identifies the EASA's objectives that are relevant to SONNX. Note that Mohamed and Jean-oup have done a similar work (yet more focused) in the context of the DeepGreen project. + - First draft to be validated at Airbus and with people from WG114. +- About the management of operators and reviews: + - /!\ Don't forget to indicate on which operator you are working (in this [table](./operator_spec_sub_wg/worksharing.md)) in order to prevent overlaps... /!\ + - Please use the gconf to do your review. And when taking account of reviews, authors shall indicate in the review form what has been taken into account (KO/OK/TBD). + +## New actions +- [X] (1202-1, Eric) Reschedule Alexandre presentation +- [ ] (1202-2, Eric) Discussion to be initiated with ONNX about the integration of our work... +- [ ] (1202-3, All) Review new operators processed by Henri + - Reminder : place your comment in a dedicated file `.md` in the "review" directory of the relevant operator +- [ ] (1202-4, All) Define the appropriate way to specify the behaviour of operators for value out of range. Apply the approach on the `div` operator, for parameters in $\mathcal R$ and, `double`and `int`. +- [ ] (1202-5, All) Define appropriate rule to handle multiples types without multiplying the specifications. +- [ ] (1202-6, Eric) Check with ONNX how to integrate our work. +## Past actions + +- [ ] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level. +- [ ] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answers given by Seb. +- [ ] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)). +- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM. +- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th. +- [ ] (2901-6, Edoardo) Check how to involve students in the specification work. +- [ ] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper. +- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`. +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops. +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... + - *Thinking...* +- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) +- [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me. +- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.) + - Discussion on-going with Loïc on the formal specification strategy... + - Meeting planned to reach a final consensus... + - Meeting done. See [minutes](../meetings/formal_methods/minutes.md). +- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx) +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + + +# 2025/01/29 +## Agenda +- Review of actions +- Other news + - Contact with Altera. +- Operator specifications + - Overview of the [`matmul`](../documents/profile_opset/matmul/matmul.md) and [`where`](../documents/profile_opset/matmul/where.md) operators + - Current status of the [operators](./operator_spec_sub_wg/SONNX_Operator_List.xlsx) and [contributors](./operator_spec_sub_wg/worksharing.md) list. +- Formal specification + - Feedback on [last meeting](./formal_methods/minutes.md). +- Other topics: + - Verification tool + - talks in 2025 +## Attendees +Jean, Julien, Eric, Mariem, Dumitru, Jean-Loup, Andreas, Sebastian, Jean-Baptiste, Tomé, João, Augustin, Anne-Sophie +
(Zoom does not give me a list of participants and I have not noted who was there...so the list if incomplete, sorry about that. Please add your name...
+ +## Minutes +- ONNX model verification tool + - This tool will implement the constraints and restrictions identified at operator and graph level + - The tool shall be part of the ONNX distribution + - Using (e.g.) DeepGreen's ONNX parser could make sense but (i) we have to be sure that we can distribute it as part of ONNX, (ii) it will bring with it part of the AIDGE framework (iii) it may be overkill with respect to the level of parsing that we actually need. + - Developing our own parser (possibly from something already available...) could also be a way to specify formally the file format (i.e., a correct ONNX file is a file that can be parsed successfully by our parser). +- Review of the comments on the [`where`](../documents/profile_opset/matmul/where.md) operator + - Sparse tensors have to be forbidden. This can be checked at the file level by looking for the usage of the SparseTensorProto class. + - In the specification of operators, we should state that such tensors cannot be used. This should be part of the operators' signatures. To be elaborated, see action [2901-1] + - Henri has given an example in Python. Should we generalize this? + - We propose to add a few example in a Jupyter Notebook for each operator. + - The examples must use ONNX. + - Those examples are not a test suite. They allow the user to get acquainted / play with of the operator. + - The behaviour of the operator in the Jupytrer notebook may be different from the one of reference implementation. + - In the Jupyter notebook, we have to be clear that the example is given for documentation only. + - See action [2901-3]. +- Specification work + - Nicolas has added LSTM to the operator's list. Dumitru proposes to help. See action [2901-4]. + - It could be useful to start thinking about the graph semantics... See action [2901-5]. + - The work moves slowly... + - We lack volunteers to do the hard work... + - We may become more visible and reach out a larger community (and hopefully, have more volunteers) once we have a first set of specification, completed documents. + - Edoardo proposes to use students to help us on the specification activity. (Jean will have one student to work on this topic) See action [2901-6]. +
Note that the initial list of interested people was pretty large... we may ask if some of them are still interested to contribute...
+- List of issues + - Anne-Sophie has removed some entries from [issues.md](../deliverables/issues/issues.md) since some of the ambiguities have been solved thanks to Sebastian. + - However, it make sense to keep them in the list since part of our job is to prevent such ambiguities... See action [2901-2]. +- On the specification of behavior in case of errors + - At operator level + - Whenever a value is out of the operator's domain, its behavior is "undefined" (e.g. xi<=0 in x for operator log(x)). In that case, the specification shall (i) indicate the domain constraint and (ii) state that the behaviour of the operator is undefined should the constraint be violated. + - For some operators, some inputs will generally be static, even though the ONNX does not enforce this. In that case, we may add a restriction to ensure that the tensor's value will be known before runtime, and add a constraints on its value to prevent runtime error. + - At graph level + - In some cases, the structure of the graph can ensure that the inputs values of an operator will always be in domain even though, they may be not in the general case. + - We may also propagate the domain constraints backward up to the inputs, and provide the user with these constraint (which would restrict the graph input domain). This could be interesting, but goes further than a verification. + - Whenever we cannot guarantee the absence of runtime error, the model wil be considered "unsafe". +- Compliance with regulatory docs. + - It would be useful to identify the constraints/reqs in the EASA's concept paper that are relevant to our work. See action [2901-7] +- Formal methods. + - See [minutes](../meetings/formal_methods/minutes.md) of FM sub-group on Feb. 28th. + - Main conclusion: We will be using Why3 as the specification language. Work will start with a training session (being planned mid-march) done by Loïc. +## New actions +- [ ] (2901-1, Eric) Check how to express constraints about SparseTensor at operator level. +- [ ] (2901-2, Anne-Sophie) Put back the issues (in the appropriate section) and add the answer given by Seb. +- [ ] (2901-3, Eric) Provide a Jupyter notebook for the `conv` operator (see [here](../documents/profile_opset/conv/tests/conv_onnx.ipynb)). +- [ ] (2901-4, Dumitru) Contact Nicolas to lend a hand on LSTM. +- [ ] (2901-5, Dumitru) Prepare a short presentation on the graph's semantics. Planned for March 12th. +- [ ] (2901-6, Edoardo) Check how to involve students in the specification work. +- [ ] (2901-7, Jean-Baptiste) Analysis of EASA's Concept Paper. +- [ ] (2901-8, Henri) Consider Eric's [remarks](../documents/profile_opset/where/reviews/eric.md) on operator `where`. +## Past actions +- [ ] (1501-1, Sebastian) Specify some operators... + - Sebastian is working on `reshape`and other ops. +- [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... + - *Thinking...* +- [X] (1501-4, All) Review the specification of the [`where` operator](../documents/profile_opset/where/where.md). Put your remarks in the [reviews](../documents/profile_opset/where/reviews/) directory (in file `.md`) or send them to me (eric). + - See Eric's [remarks](../documents/profile_opset/where/reviews/eric.md). +- [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) +- [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me. +- [X] (1501-7, Eric) Check how to communicate with ONNX to sort out ambiguities... + - The best solution is probably to use the [ONNX] test suite, which covers operators and graph. "Just in case", I have also contacted RAM at ONNX. + - After a discussion with Ram : We can use the LFx slack channel (Operator SIG). Two contacts : G. Ramalingam and Justin Chu +- [X] (1508-1, Eric) Send the list of questions to the WG 114 leader. + - List of questions sent to the WG114 chairwoman on Jan. 16th. + - They'll analyze them and come back to us. +- [X] (1812-2, Eric) Complete the discussion about numerical accuracy and error management. + - See mail dated 19/12. + - See [new version of the document](./errors/error_specification.md) +- [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.) + - Discussion on-going with Loïc on the formal specification strategy... + - Meeting planned to reach a final consensus... + - Meeting done. See [minutes](../meetings/formal_methods/minutes.md). +- [ ] (1812-5, All) Indicate on which operator one can contribute (writer/reviewer). Put your id in this [table](./operator_spec_sub_wg/worksharing.md) The list of operators with their "complexity" and links to the ONNX doc are in this [Excel sheet](./operator_spec_sub_wg/SONNX_Operator_List.xlsx) +- [ ] (1812-6, All) Check legal aspects of contributing to the SONNX effort ("clearance") +- [X] (0412-4, Thiziri, Nicolas, Jean, Sebastian, Jean-Loup) Review of the [updated version of CONV2D](../documents/conv_specification_example/README.md) + - Review from Thiziri to be received on 2024/12/20. + - *No answer => Closed* +- [ ] (0412-6, Eric) Create a sub working group to analyze the existing standard in a systematic way... + - Contribution of Anne-Sophie. But WG to be set. + + # 2025/01/15 ## Agenda - Edoardo's presentation about the "Evaluation and improvement of SW verifiers on FP Neural Networks" @@ -10,10 +1782,10 @@ - Discussion on the behavior in the presence of errors (see action 1812-2) ## Attendees -Eduardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong, Jean-Loup [sorry, some names are probably missing ; I don't know how to obtain the list of attendees using LFAI...] +Edoardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong, Jean-Loup [sorry, some names are probably missing ; I don't know how to obtain the list of attendees using LFAI...] ## Minutes -- Edorado's slides can be found [here](./slides/2025-01-15-Edoardo_Manino_SONNX_slides.pdf). +- Edoardo's slides can be found [here](./slides/2025-01-15-Edoardo_Manino_SONNX_slides.pdf). - Status of the current outoputs of our WG. We are a bit late with respect to the initial plan... - Concerning the specification work, we can take `conv` as an example, even though it may be later modified thanks to the lessons learnt on other operators. - We have to find people to work on the specification. See action [1501-1] and [1501-2]. @@ -34,18 +1806,18 @@ Eduardo, Eric, Jean, Mariem, Sebastian, Augustin, Dumitru, Henri, Nicolas, Cong, ## New actions - [ ] (1501-1, Sebastian) Specify some operators... - [ ] (1501-2, Eric & Jean) Find a way to involve more people in the specification work... -- [ ] (1501-3, all) Drop an e-mail to Mariem should you be interested in the work on formal methods +- [X] (1501-3, all) Drop an e-mail to Mariem should you be interested in the work on formal methods - [ ] (1501-4, All) Review the specification of the [`where` operator](../documents/profile_opset/where/where.md). Put your remarks in the [reviews](../documents/profile_opset/where/reviews/) directory (in file `.md`) or send them to me. - [ ] (1501-5, Anne-Sophie) Move issues to the "graph" part when they concern the graph (and not a specific operator) - [ ] (1501-6, All) Review issues reported by Anne-Sophie in file [issues.md](../documents/issues.md). Put your remarks in the [reviews](../deliverables/issues/reviews/) directory (in file `.md`) or send them to me. - [ ] (1501-7, Eric) Check how to communicate with ONNX to sort out ambiguities... -- [X] (1508-1, Eric) Send the list of questions to the WG 114 leader. +- [ ] (1508-1, Eric) Send the list of questions to the WG 114 leader. - List of questions sent to the WG114 chairwoman on Jan. 16th. ## Past actions - [X] (1812-1, Mariem et Eric) Process reviews of `conv`. - Done. Spec moved [here](../documents/profile_opset/conv/) -- [X] (1812-2, Eric) Complete the discussion about numerical accuracy and error management. +- [ ] (1812-2, Eric) Complete the discussion about numerical accuracy and error management. - See mail dated 19/12. - [ ] (1812-3, Mariem) Complete the formal specification of `conv` with the help of FM experts (Augustin, Christophe, Cong, Eduardo, Loïc, etc.) - Discussion on-going with Loïc on the formal specification strategy... @@ -636,3 +2408,11 @@ Other (off-meeting) - [ ] <10/07>(eric+jean) Organize some meetings as “advisory boards” - [x] <21/08> (Eric) Convert the SOW to markdown + + diff --git a/safety-related-profile/meetings/errors/01_what_is_the_issue.md b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md similarity index 92% rename from safety-related-profile/meetings/errors/01_what_is_the_issue.md rename to safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md index 2ee0a692..d5e61701 100644 --- a/safety-related-profile/meetings/errors/01_what_is_the_issue.md +++ b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.md @@ -9,7 +9,7 @@ This document contains various elements concerning computation errors and their - *Floating-point* numbers are encoded on a finite number of bits. Therefore, all real numbers can't be represented exactly. Non-representable numbers must "mapped" (rounded) to representable numbers according to a rounding strategy. - Concerning operations, the principle (IEEE 754) is that the result of an operation on rounded values shall be the same as the result that would be obtained by rounding the result computed using exact values. The exact statement, from [IEEE754] is the following: > "[...] each of the computational operations specified by this standard that returns a numeric result shall be performed as if it first produced an intermediate result correct to infinite precision and with unbounded range, and then rounded that intermediate result, if necessary, to fit in the destination’s format" -- In addition, due to the finitness of the nuber of bits, overflow and underflow conditions may occur. Conditions may apply on input values to prevent runtime errors. At least, runtime errors shall be signaled. In principle, these preconditions should be be part of the specification of the network (otherwise, there may be conditions in which the model doesn't produce any sensible result). +- In addition, due to the finiteness of the number of bits, overflow and underflow conditions may occur. Conditions may apply on input values to prevent runtime errors. At least, runtime errors shall be signaled. In principle, these preconditions should be be part of the specification of the network (otherwise, there may be conditions in which the model doesn't produce any sensible result). ## Non-reproductibility - Depending on some internal conditions, the same piece of code given the same inputs may lead to different sequences of operations, then different numerical results due to the non associativity of floating point operations. Those internal conditions may be difficult to determine in the absence of a full description of the execution platform. See for instance [Dem-15, Col-15]. In [Dem-15], this effect is studied for a simple parallel summation. @@ -18,10 +18,10 @@ This document contains various elements concerning computation errors and their ## IEEE-754 and non IEEE-754 data types -IEEE 754 provides a set of guarantees about floating point computations [IEEE-754], howwever : +IEEE 754 provides a set of guarantees about floating point computations [IEEE-754], however : - Are all hardware (GPUs, accelerators) implementing the IEEE-754 standard? - - No: NVIDUA uses TF32. + - No: NVIDIA uses TF32. - Are all hardware devices IEEE-754 compliant (GPUs, FPGA and ASIC accelerators)? - Some of the data types used in machine learning to not belong to the IEEE 754 standards. For example: [BF16](https://en.wikipedia.org/wiki/Bfloat16_floating-point_format) (bfloat16), [TF32](https://en.wikipedia.org/wiki/TensorFloat-32) (TensorFlow 32 bits) - What are the properties of those representations (do they respect the same principle as IEEE 754 numbers?) @@ -44,7 +44,7 @@ IEEE 754 provides a set of guarantees about floating point computations [IEEE-75 # Relation between accuracy and ML properties... -This section deals with the relation between computaton errors due to (e.g.,) rounding, and the actual expected properties of the ML model. The rationale is that it *may be the case* that bitwise reproducibility is "overkill" with respect to the actual performance of the ML model. +This section deals with the relation between computation errors due to (e.g.,) rounding, and the actual expected properties of the ML model. The rationale is that it *may be the case* that bitwise reproducibility is "overkill" with respect to the actual performance of the ML model. ## About the effects of floating point errors on ML model performance... - To what extent is the question of computation errors pertinent with respect to the other sources of errors in Machine Learning algorithms (or "how do computation errors compare to other sources of errors")? @@ -70,25 +70,25 @@ Complex operations are described using a small corpus of basic operators and fun ## Standards -All programming languages come with a set of basic mathematical operators, either directly implemented by the hardware or implemented in a mathematical library (e.g., ``libm``). Libraries also exist for high-level function such as Basic Linear Agebra (e.g., [BLAS](https://www.netlib.org/blas/), [LINPACK](https://www.netlib.org/linpack/),etc.). Some libraries are provided by chip manufacturers in order to take the most out of their hardware ([cuBLAS](https://docs.nvidia.com/cuda/cublas/index.html), Intel math lib) +All programming languages come with a set of basic mathematical operators, either directly implemented by the hardware or implemented in a mathematical library (e.g., ``libm``). Libraries also exist for high-level function such as Basic Linear Algebra (e.g., [BLAS](https://www.netlib.org/blas/), [LINPACK](https://www.netlib.org/linpack/),etc.). Some libraries are provided by chip manufacturers in order to take the most out of their hardware ([cuBLAS](https://docs.nvidia.com/cuda/cublas/index.html), Intel math lib) ### Mathematical libraries - ``libm`` - The newlib mathematical library does not give the accuracy of operations. (Note that the way mathematical operations are described could be inspiring) In addition, as stated in [Dar-06] "current [as of 2006] libm implementation do not always return the floating point number that is closest to the exact mathematical result. As a consequence, different libm implementation will return different results for the same input, which prevents fill portability for floating-point applications". The same claim can be read in [Gla-24]: - > The IEEE 754 standard, even in its latest 2019 revision [17], does not require correctly rounded mathematical functions, it only recommends them. In turn, current athematical libraries do not provide correct rounding, which is the best possible result. Thus, users might get different results with different libraries, or different versions of the same library. This can have dramatic consequences: for example missed collisions in the Large Hadron Collider [5] or reproducibility issues in neuroimaging [13]. - - BLAS and other linear algrebra libraries + > The IEEE 754 standard, even in its latest 2019 revision [17], does not require correctly rounded mathematical functions, it only recommends them. In turn, current mathematical libraries do not provide correct rounding, which is the best possible result. Thus, users might get different results with different libraries, or different versions of the same library. This can have dramatic consequences: for example missed collisions in the Large Hadron Collider [5] or reproducibility issues in neuroimaging [13]. + - BLAS and other linear algebra libraries - BLAS is not accurately rounded. For an accurately rounded BLAS, see e.g., [Cho-16]. - cuBLAS - cuBLAS is designed so that "all [...]] API routines from a given toolkit version, generate the same bit-wise results at every run when executed on GPUs with the same architecture and the same number of SMs. However, bit-wise reproducibility is not guaranteed across toolkit versions because the implementation might differ due to some implementation changes." ([cuBLAS documentation](https://docs.nvidia.com/cuda/cublas/index.html#using-the-cublas-api), section 2.1.4). - The precision of operators is not specified. See e.g, ``gemm`` in section [2.7.1 ](https://docs.nvidia.com/cuda/cublas/index.html#cublas-t-gemm). - [Intel MKL BLAS ](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/overview.html) - - See e.g.., the [``gemm``](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/cblas-gemm-001.html) function: no inidcations whatsoever about accuracy. + - See e.g.., the [``gemm``](https://www.intel.com/content/www/us/en/docs/onemkl/developer-reference-c/2024-2/cblas-gemm-001.html) function: no indications whatsoever about accuracy. ## Programming languages How are floating point operations specified in programming languages? Here are a few examples: - C - - In the C99 standard [C99], nothing is said about the accuraty of operator. For instance, for the sin operator, the specification is the following: + - In the C99 standard [C99], nothing is said about the accuracy of operator. For instance, for the sin operator, the specification is the following: ``` Description The sin functions compute the sine of x (measured in radians). @@ -104,7 +104,7 @@ Note 1: the result of a computation also depends on the compiler, on the hardwa Note 2: an interesting quotation from [Mul-10]: > As pointed out by David Goldberg [...] ) all these uncertainties make it impossible, in many cases, to figure out the exact semantics of a floating-point C program just by reading its code. -Several examples of strange behaviours are given in the book. One example considers the case where a symmetric function (e.g., a function $f(x,y)$ computing the distance between $x$ and $y$) may be compiled in an asymetrical way using, e.g., ``fma`` in such a way that $f(x,y) \neq f(y,x)$, potentially breaking algorithms relying on the symmetry of the fonction (such as sorting algorithms). +Several examples of strange behaviours are given in the book. One example considers the case where a symmetric function (e.g., a function $f(x,y)$ computing the distance between $x$ and $y$) may be compiled in an asymmetrical way using, e.g., ``fma`` in such a way that $f(x,y) \neq f(y,x)$, potentially breaking algorithms relying on the symmetry of the function (such as sorting algorithms). ### Industrial standards @@ -131,7 +131,7 @@ What do the industrial standards say about computation errors (in space, aeronau #### Aeronautical standards - The DO-178C [DO-178C] doesn't say much besides that the source code must be correct with respect to floating point arithmetic (§6.3.4.f) (the term "floating point" appears only once). -- (May refer to the MOPS, but it is unlikely that we will find anything concerning the allocateion of errors to computations.) +- (May refer to the MOPS, but it is unlikely that we will find anything concerning the allocation of errors to computations.) #### Automotive standards - The ISO 26262-6 [ISO26262] ("Product development at the software level") doesn't say much about floating point computations. @@ -141,7 +141,7 @@ What are our needs concerning computation accuracy, and **for what purpose**? ## Industrial needs and requirement on SONNX - [**Needs**] The executions of a SONNX model shall be reproducible to support debugging activities. - - Reproducibility means that given the same inputs, all computations shall give the same ouputs.\ + - Reproducibility means that given the same inputs, all computations shall give the same outputs.\ The level of reproducibility be defined by (i) a bound on the errors on the outputs, (ii) the supported variability on the target SW or HW.\ For instance, one may require that "the model shall give bitwise identical results when executed on the same HW architecture" or "the model shall give results $\pm \epsilon$ when executed on the same HW architecture", etc.\ Note that this requirement is only about reproducibility, i.e., the results may be perfectly reproducible but completely wrong with respect to the model "semantics". @@ -154,7 +154,7 @@ In particular, this means that the (meta-)model (i.e., the SONNX standard) must For instance, using floating point can lead to unsound verification results in the sense that a model that is robust in $\mathbb{R}$ (i.e, verified formally to be robust considering values in R) may not be robust when implemented using finite precision numbers [???]. In this example, the MLMD would have to be implemented in R (if it were possible) for the robustness property to be preserved. *Note (a): "Reproduction" is different from "replication" because the former concerns the relation between different executions of the same model implementation, whereas the latter concerns the relation between the model and its implementation(s).*\ -*Note (b): Instead of specifiying requirements about accuracy and precision, couldn't we just specify implementation requirements, i.e, the way computations must be done.* +*Note (b): Instead of specifying requirements about accuracy and precision, couldn't we just specify implementation requirements, i.e, the way computations must be done.* ## Certification "needs" @@ -228,7 +228,7 @@ From the previous needs, what are the requirements applicable to SONNX? The foll # Means of analysis techniques and tools - What are the technical means available to estimate the impact of errors on results? (e.g., fluctuat, CADNA...) - - See [Beu-24, Ch. 4] who proposes forward / backward error estiation for neural networks, taking into account nn linear activation functions. + - See [Beu-24, Ch. 4] who proposes forward / backward error estimation for neural networks, taking into account nn linear activation functions. - Do we need the intervention of some FP experts? Who? diff --git a/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf new file mode 100644 index 00000000..62c708c6 Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.pdf differ diff --git a/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png new file mode 100644 index 00000000..f9f02074 Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/01_what_is_the_issue.png differ diff --git a/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md b/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md new file mode 100644 index 00000000..6091609f --- /dev/null +++ b/safety-related-profile/meetings/numerical accuracy/2025-04-11_Meeting_with_Franck.md @@ -0,0 +1,76 @@ +### **Participants** +- **Franck VEDRINE** (CEA, developer of the Fluctuat tool) +- **Mariem TURKI** (IRT St-Ex) +- **Jean SOUYRIS** (Airbus) +- **Eric JENN** (IRT St-Ex) + +### **Subject:** +- Introduction of the SONNX working group to Franck +- Open discussion and potential directions + +### **Discussion Summary** + +- **Initial Focus:** + - We will first concentrate on networks using: + - **Integer data (quantized networks)**, e.g., int8. + - **Floating-point data** (16, 32, and 64-bit), provided they comply with the IEEE standard. + +- **Analogy with SCADE Operators:** + - There are parallels between our work on graph operators and SCADE operators. The requirements are quite similar, but differences lie in: + - The sheer volume of operations (much higher in neural networks). + - The challenge of assessing the impact of computational errors on function performance (e.g., how does an error in an operator affect the final decision?). + - In any case, it seems difficult to specify error objectives (relative or absolute)* at the operator level. + - Even in more conventional cases, such requirements are rarely defined *a priori*, except in specific scenarios. + +- **Current Approach:** + - More "pragmatic"—determining the maximum error that can be guaranteed with a given effort (*best effort*). + - This error becomes the *de facto* specification, as it can be accounted for at the system level (e.g., for filtering functions). + +- **Error Evaluation Considerations:** + - Must account for: + - **Method-related errors** + - **Implementation-related errors** + - Currently, SONNX does **not** specify the method or implementation, so this approach is not directly applicable at this level. + +- **Reference Implementation Level:** + - An **error estimate** can be provided for the reference implementation. + - **Caution:** + - This error might only be achievable in a simple implementation and degrade in more complex, optimized versions. + - This could lead to specifying unrealistic error bounds. + - **Proposal:** Provide estimates for multiple implementations: + - A simple one (our "reference implementation" via Why3), easier to analyze but potentially less efficient. + - A more complex, optimized one. + - **Key Points:** + - In critical domains, implementations should remain relatively simple. + - Otherwise, applicants are free to develop their own implementation and apply the recommended method. + +- **Error Evaluation Categories:** + To handle a wide range of cases (including complex ones without formal estimates), evaluations could be categorized as: + - **Bronze:** Incomplete error assessment. + - **Silver:** Abstract interpretation (Fluctuat). + - **Gold:** Axiomatic proof. + +- **Alternative Approach:** + - We could also simply describe the error estimation method. + - However, whenever possible, we will aim to provide tools for estimating the error, potentially as: + - An **analytical expression**. + - A **calculation program** (if the error depends on input tensor characteristics, e.g., dimensions and size). + +- **Input Domain Constraints:** + - In some use cases, input parameter ranges may be bounded and known (e.g., values in [-1, 1]). + - Such constraints could improve error estimation accuracy. + - Further study is needed to determine when these apply. + +- **Benefits of Error Estimation:** + - Facilitates model debugging by identifying error sources. + - Enables post-mortem analysis/diagnostics in case of failure. + - Provides insights into the origin of output errors. + +- **Next Steps with Franck:** + - Franck expressed interest in the topic and agreed to join the working group. + - **Actions:** + - [X] Send meeting invitations. + - [X] Share document links. + - [X] Grant Git access. + + diff --git a/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md b/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md new file mode 100644 index 00000000..634988f6 --- /dev/null +++ b/safety-related-profile/meetings/numerical accuracy/2026-02-17 - DeepGreen - Accuracy.md @@ -0,0 +1,80 @@ +Date : + +--- + +## Participants + +* Yves THUILLIER (CEA) +* Franck VEDRINE (CEA) +* Augustin LEMESLE (CEA) +* Pierre GAILLARD (CEA) +* Jean SOUYRIS (Airbus) +* Eric JENN (IRT St-Ex) +* Mariem TURKI (IRT St-Ex) + +--- + +## Purpose of the Meeting + +* Presentation of the various works carried out by CEA on the numerical precision of neural networks. +* Discussion on the overall articulation of the work, particularly its relationship with the objectives of the SONNX working group and those of the Aidge platform. + +--- + +## Minutes + +### Presentation of the Context + +* Brief presentation of the SONNX group’s work +* Brief presentation of the Aidge platform + +### Presentation of the Work on Numerical Precision + +* Presentation by Yves (see slides) +* Presentation by Franck (see slides) +* Presentation by Augustin (see slides) + +--- + +## (Attempted) Summary + +From SONNX’s perspective, the objective is to ensure that the specification is sufficiently precise to: + +(i) capture the intentions of the model designer, +(ii) preserve a set of properties that have been verified on the model (e.g., ML performance, robustness, etc.), +(iii) enable the model implementer to preserve (i) and (ii). + +Regarding (i), SONNX describes the semantics of operators *while taking into account certain implementation-related aspects.* In general, it is simply impossible to implement exactly a specification that would be purely mathematical. Therefore, the “user” must be aware of certain consequences related to implementation choices. + +In practice, each operator is specified by explicitly describing the effects of IEEE special values and domain bounds, but *the precision of the operators is not specified.* This is, of course, debatable since, *in principle*, not specifying the precision of the result of a numerical computation amounts to saying nothing at all. + +However, it is difficult to specify precision in absolute terms without referring to the original need (i.e., linking it to the network’s own performance) and without over-specifying. This is particularly true in ML, where: + +1. The performance of the function (e.g., network accuracy) does not depend very directly on computational precision (see quantization). +2. Performance (latency) plays a major role given the large number of computations involved, which encourages numerous optimizations. + +In reality, mathematical libraries that provide guaranteed precision values are relatively rare (please correct me if this is wrong). + +The approach proposed by SONNX, as described by Franck, consists in providing methodological means and tools to evaluate a guaranteed formal error, rather than specifying it. + +The analytical formulation of precision depends on the chosen computation method (“algorithm”). In the example of ( \tanh ) presented by Franck, a computation method is proposed that is generally more precise than the usual formulation. However, it is certainly not the only or the best method; it is one method, and it is the one that will be implemented in the SONNX reference implementation. + +One could consider that any implementation of ( \tanh ) should be at least as precise as the reference implementation, but in that case, we would return to the idea of specifying precision. Instead, it will be considered as indicative guidance. + +It should also be noted that certain characteristics of value domains inherent to neural networks are not taken into account (e.g., the fact that values are often normalized to belong to the interval ([0,1])). + +The analysis of error introduction is analytical (sometimes defined recursively with respect to the number of dimensions) and conservative. Again, there is no single unique way to perform the analysis, and the choice of method is mainly guided by complexity and effort considerations. The user remains free to perform a more precise analysis. + +(It should also be noted that an analysis of error propagation is proposed, but this is mainly for informative purposes.) + +Franck also proposes a tooling approach (a class library) that, by simply changing the variable types, makes it possible to obtain an error estimate for a given algorithm implementation (code). If I understood correctly, this could replace manual analysis, although it cannot be fully automated, as certain choices require user intervention. + +The methods and tools proposed by Yves operate through code instrumentation (via the same operator overloading mechanism used by Franck, if I understood correctly). Error computation is performed trajectory by trajectory by introducing a random perturbation in each computation (rounding upward or downward). The method is slightly more complex in order to avoid being overly conservative (for example, the error on the computation of the expression ( a - a ) must be zero regardless of the error made in evaluating ( a )). + +The result is not an upper bound on the error, unlike Franck’s approach, but rather an error closer to empirical observation. It notably accounts for compensation effects that may occur during computations. Yves’ method makes it possible to compute errors on complete graphs. + +The method proposed by Augustin does not aim to calculate the introduction and propagation of errors, but rather to provide bounds on the values taken by the outputs of a network (final or intermediate), assuming the operators are ideal (i.e., mathematically exact). However, the effects of errors are taken into account in the computation of abstract domains. Thus, if an interval ([m, M]) is computed, the values of ( m ) and ( M ) are determined so that they are valid lower and upper bounds, considering that they were computed using floating-point numbers. + +The objective of the PyRat tool is therefore to provide bounds on network values, which is useful for assessing network robustness, a property often required for safety. Since operators are considered mathematically perfect (i.e., free of errors), it is possible that conclusions drawn by PyRat on the abstract model may not be preserved in the implementation of the model. + +One could imagine combining the approach aimed at estimating upper error bounds (Yves and Franck) with the abstract interpretation implemented by Augustin: each “ideal” computation would take into account the error (worst-case or average) computed using Yves’ and Franck’s methods. diff --git a/safety-related-profile/meetings/numerical accuracy/README.md b/safety-related-profile/meetings/numerical accuracy/README.md new file mode 100644 index 00000000..90dc86ab --- /dev/null +++ b/safety-related-profile/meetings/numerical accuracy/README.md @@ -0,0 +1,5 @@ +This directory contains minutes of discussions dedicated to the question of numerical accuracy, specification of FP operators, etc. : +- A [few elements](01_what_is_the_issue.md) about the issue (Eric) +- The [slides](slides-06-11.md) presented on 11/06 WG meeting (Eric) +- The [minutes of the meeting](./2025-04-11_Meeting_with_Franck.md) with Franck VEDRINE (CEA) +- The [slides presented by Franck VEDRINE](./franck_slides_19_11_25.pdf) that depict the approach followed in SONNX. diff --git a/safety-related-profile/meetings/errors/images/ada_accuracy.png b/safety-related-profile/meetings/numerical accuracy/images/ada_accuracy.png similarity index 100% rename from safety-related-profile/meetings/errors/images/ada_accuracy.png rename to safety-related-profile/meetings/numerical accuracy/images/ada_accuracy.png diff --git a/safety-related-profile/meetings/numerical accuracy/images/stability.png b/safety-related-profile/meetings/numerical accuracy/images/stability.png new file mode 100644 index 00000000..877c81cc Binary files /dev/null and b/safety-related-profile/meetings/numerical accuracy/images/stability.png differ diff --git a/safety-related-profile/meetings/numerical accuracy/numerical_stability.md b/safety-related-profile/meetings/numerical accuracy/numerical_stability.md new file mode 100644 index 00000000..a37d2984 --- /dev/null +++ b/safety-related-profile/meetings/numerical accuracy/numerical_stability.md @@ -0,0 +1,112 @@ + + +## Python Example: Stable vs Unstable Computation + +We'll compute the function: +$f(x) = \frac{1 - \cos(x)}{x^2}$ as $x \rightarrow 0$. + +This form is **numerically unstable** due to cancellation (subtracting two nearly equal numbers). + +### Stable Alternative: +Use the Taylor expansion: + +$1 - \cos(x) \approx \frac{x^2}{2} - \frac{x^4}{24}$ + +So $f(x) \approx 0.5 - \frac{x^2}{24}$ + +### Python Code + +```python +import numpy as np +import matplotlib.pyplot as plt + +# Unstable implementation +def f_unstable(x): + return (1 - np.cos(x)) / x**2 + +# Stable implementation using Taylor series +def f_stable(x): + return 0.5 - x**2 / 24 + +# Values close to 0 +x_vals = np.logspace(-16, -1, 100) +y_unstable = f_unstable(x_vals) +y_stable = f_stable(x_vals) + +plt.figure(figsize=(10, 6)) +plt.plot(x_vals, y_unstable, label="Unstable", color='red') +plt.plot(x_vals, y_stable, label="Stable", linestyle='--', color='green') +plt.axhline(0.5, color='blue', linestyle=':', label="True Limit = 0.5") +plt.xscale('log') +plt.xlabel("x") +plt.ylabel("f(x)") +plt.title("Numerical Stability Example") +plt.legend() +plt.grid(True) +plt.tight_layout() +plt.show() +``` +On the following picture, you can see that the unstable version (red line) becomes wildly inaccurate as x gets very small, due to numerical errors. The stable version (green dashed line) stays close to the true limit of 0.5 (blue dotted line). + +![image](./images/stability.png) + + +## Cheat sheet + +### Common Pitfalls & Fixes + +| ❌ Unstable Pattern | ✅ Stable Alternative | +|-----------------------------------------|--------------------------------------------------| +| `a - b` where `a ≈ b` | Algebraic manipulation or Taylor expansion | +| Matrix inversion: `inv(A) @ b` | Use `np.linalg.solve(A, b)` | +| Classical Gram-Schmidt | Use **Modified Gram-Schmidt** | +| Small `x`: `(1 - cos(x)) / x^2` | Use series: `0.5 - x^2 / 24` | +| Large/small values in matrix | Rescale or normalize data | + +--- + +### Stability Best Practices + +- Avoid subtracting nearly equal numbers +- Use backward stable algorithms +- Normalize inputs when scales vary wildly +- Use high-precision types (`float64`, `mpmath`) +- Prefer library routines (NumPy, SciPy, LAPACK) + + +### Tools to Assess Stability + +| Tool | Description | +|----------------------------------|----------------------------------------------------| +| `np.linalg.cond(A)` | Matrix condition number (sensitivity) | +| `np.seterr(all='warn')` | Catch floating-point warnings | +| Compare multiple methods | Sanity check results across techniques | +| Refine resolution or input size | See how solution evolves (esp. in ODEs/PDEs) | + +--- + +### Stability Testing Techniques + +#### Forward Error Analysis +Compare computed output to exact result: +``` +Forward Error = || y_computed - y_exact || +``` + +#### Backward Error Analysis +Show computed output is exact for a nearby input: +``` +A * x_computed = b + δb → Backward Stable +``` + +#### Condition Number +Estimates sensitivity of problem: +``` +κ = ||A|| * ||A⁻¹|| (depends on chosen norm) +``` +Large `κ` ⇒ ill-conditioned ⇒ sensitive to input errors + + + + + diff --git a/safety-related-profile/meetings/errors/slides-06-11.md b/safety-related-profile/meetings/numerical accuracy/slides-06-11.md similarity index 100% rename from safety-related-profile/meetings/errors/slides-06-11.md rename to safety-related-profile/meetings/numerical accuracy/slides-06-11.md diff --git a/safety-related-profile/meetings/errors/slides-06-11.pdf b/safety-related-profile/meetings/numerical accuracy/slides-06-11.pdf similarity index 100% rename from safety-related-profile/meetings/errors/slides-06-11.pdf rename to safety-related-profile/meetings/numerical accuracy/slides-06-11.pdf diff --git a/safety-related-profile/meetings/presentation_proposals.md b/safety-related-profile/meetings/presentation_proposals.md index a4938377..d7004375 100644 --- a/safety-related-profile/meetings/presentation_proposals.md +++ b/safety-related-profile/meetings/presentation_proposals.md @@ -4,8 +4,10 @@ | Code generation for neural networks | Sebastian Boblest (Bosch) | 2024/11 | 2024/12/18 | | ONNX MLIR | Alexandre Eichenberger (IBM) | 2024/10 | 2025/02/12 | | Evaluation and improvement of SW verifiers on FP Neural Networks | Edoardo Manino (Manchester U) | 2024/11 | 202(/01/15) | - - +| About quantization and ONNX in Airbus' context | Alex Digonnet | 2025/05/07 | 2025/05/07 | +| RMT | Jean-Baptiste | 2025/05/07 | | +|Specification of concat| Salomé Marty Laurent | 2025/05/21 | 2025/05/21 | +| TITLE| Dumitru Potop | 2025/06/18 | TBC | # Meetings - 2024/11/20 @@ -13,5 +15,15 @@ - 2024/12/18 : Sebastian - 2025/01/15 : Edoardo - 2025/01/29 -- 2025/02/12 : Alexandre -- 2025/02/26 +- 2025/02/12 +- 2025/02/26 (Eric on vacations) +- 2025/03/12 +- 2025/03/26 +- 2025/04/09 : Alexandre => to be rescheduled +- 2025/04/23 +- 2025/05/07 : Jean-Baptiste ; Alex +- 2025/05/21 : Salomé +- 2025/09/10 : +- 2025/10/08 : +- 2025/10/22 : +- 2025/11/05 : diff --git a/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf b/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf new file mode 100644 index 00000000..03ee18d9 Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-03-12-Dumitru on ONNX-graph-semantics.pdf differ diff --git a/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf b/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf new file mode 100644 index 00000000..936063af Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-05-07-Alex-DIGONNET-SONNX quantization representation formats.pdf differ diff --git a/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf b/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf new file mode 100644 index 00000000..e080e8b0 Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-05-21-Salome_MARTY_LAURENT_concat_operator_specification.pdf differ diff --git a/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf b/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf new file mode 100644 index 00000000..e6c35376 Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-07-16-Salome_MARTY_LAURENT_add_operator_specification.pdf differ diff --git a/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf b/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf new file mode 100644 index 00000000..5915ed8b Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-19 - Jean- SONNX V&V .pdf differ diff --git a/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf b/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf new file mode 100644 index 00000000..69f075e0 Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-20-Franck-numerical-accuracy.pdf differ diff --git a/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf b/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf new file mode 100644 index 00000000..11acd612 Binary files /dev/null and b/safety-related-profile/meetings/slides/2025-11-20-Jean-verification-plan.pdf differ diff --git a/safety-related-profile/meetings/slides/AIDGE.pptx b/safety-related-profile/meetings/slides/AIDGE.pptx new file mode 100644 index 00000000..ef20a95d Binary files /dev/null and b/safety-related-profile/meetings/slides/AIDGE.pptx differ diff --git a/safety-related-profile/meetings/slides/README.md b/safety-related-profile/meetings/slides/README.md index 2ae9ffc2..4fe58abb 100644 --- a/safety-related-profile/meetings/slides/README.md +++ b/safety-related-profile/meetings/slides/README.md @@ -1,3 +1,5 @@ Slides presented during our periodic meetings. - [Slides presented by Sebastian on Bosch's code generatuoin tool](./2024-12-18-Embedded_AI_Coder_Bosch_Boblest.pdf) - [Slides presented by Edoardo on bit-precise neural network verification](./2025-01-15-Edoardo_Manino_SONNX_slides.pdf) +- [Slides presented by Dumitru on ONNX graph semantics](./2025-03-12-Dumitru%20on%20ONNX-graph-semantics.pdf) + diff --git a/safety-related-profile/meetings/testing/2025-07-29-tests.md b/safety-related-profile/meetings/testing/2025-07-29-tests.md new file mode 100644 index 00000000..ba986c88 --- /dev/null +++ b/safety-related-profile/meetings/testing/2025-07-29-tests.md @@ -0,0 +1,32 @@ +**Objective of the meeting** +- Establish a technical link between the work on test definition and generation done by Christian using Hypothesis and the work of SONNX. + +**Minutes** +-  Brief presentation of the work of SONNX. The full slide deck can be found [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/Other_meetings/SONNX%20-%20WG114.pdf) +- Brief presentation of the [Hypothesis](https://hypothesis.readthedocs.io/en/latest/reference/strategies.html) test framework and its use to generate test for ONNX ops. Christian work is available [here](https://github.com/cbourjau/onnx-tests). The list of operators already covered is [there](https://github.com/cbourjau/onnx-tests/blob/main/report/coverage.md). +-  Basically, Hypothesis allows to describe conditions on the inputs of a function and generate automatically (more or less randomly) inputs compliant with these conditions. This facilitate the generation of large test sets, even though it does not provide any coverage "guarantee" with respect to some set of equivalence classes (themselves defined with respect to some test objective).  +- The first tests carried out by Christian show that there are still errors in ONNX runtime. Most of the errors activated by those tests actually concern non implemented functions (with respect to types), or boundary values corresponding to very unlikely situations (dimensions equal to 0). +- ORT comes with a bunch of tests, but they are hand crafted, which means the coverage is currently not satisfying completely . +- We agreed that there is a strong relation between the specification developed in SONNX, since this specification defines the domain of the operators (so-called "constraints" in the SONNX spec). +-  The Hypothesis constraints (or part of them) could be generated from the formal specification. + +[ ] (SONNX) Try to do the exercise on the Conv2D operator for which we both have the formal specification and the Hypothesis tests. The same could be done for CONCAT (see next action). +[ ] (Christian) Provide an Hypothesis definition of test cases for the CONCAT operator. + +- The reference implementation to be developed in SONNX could be used as a possible oracle for Hypothesis tests. +- Note that during the development of the SONNX profile, the tests could also be used to detect errors  the specification: a test that does not pass (i.e., there is a discrepancy between our reference implementation and, let's say, the ORT) may reflect either an error in the spec or in the implementation. It will be up to the analysis to identify the root cause of the discrepancy. +- Eric mentioned that the [DeepGreen](https://deepgreen.ai/en) project, which is developing the [AIDGE](https://gitlab.eclipse.org/eclipse/aidge/aidge) platform, is also developing tests cases. There may be an opportunity to share some of the work. +- We have had a discussion about the generation of tests for a complete graph using Hypothesis. In that case, the difficulty is to define the ODD using Hypothesis. More often than not the ODD is actually defined by a representative set of samples. For high-dimensional inputs, defining conditions on the inputs is usually very difficult (not impossible depending on the application). +- Discussion about the demonstration of semantic equivalence in the presence of graph transformation. Inspiration could be obtained from what is done on compilers (e.g., LLVM). + +[ ] Eric to provide contact to the [DeepGreen](https://deepgreen.ai/en) project + +[ ] Eric to provide pointers to te SONNX documentation +=>   +- The repository is [here](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile) (this is Eric's repo ; the ONNX repo is not up to date). +- You will find [there](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/meetings/minutes.md) the minutes of all our meetings.  +- The set of operators is [here](http://safety-related-profile/documents/profile_opset). /!\ This is a work in progress /!\ +- Note that [conv](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/conv.md) and [concat](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/concat/concat.md) are the most up to date specifications.  +- All the operators should comply with a certain set of [guidelines](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/guidelines.md). +- The formal specifications are [here](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/why3/standard_convolution.mlw) for conv and [there](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_opset/conv/why3/standard_convolution.mlw) for concat. They are expressed using the WhyMl language. +- There is more material, about the semantics of the [graph](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/documents/profile_graph/graph.md) execution, about [error conditions](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/meetings/errror%20conditions), about [numerical accuracy](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/meetings/numerical%20accuracy), etc. diff --git a/safety-related-profile/sonnx/README.md b/safety-related-profile/sonnx/README.md new file mode 100644 index 00000000..b712ba72 --- /dev/null +++ b/safety-related-profile/sonnx/README.md @@ -0,0 +1,58 @@ +# Directory Structure + +This repository contains both formal and informal specifications for operators and graph, documentation, tests, and verification tools for various operators. The project is organized as follows: + +## General Structure + +```plaintext +sonnx/ +├── ops/ +│ ├── spec/ +│ │ ├── informal/ +│ │ │ ├── common +│ │ │ ├── add/ +│ │ │ │ ├── assets +│ │ │ │ ├── reviews +│ │ │ │ ├── tests +│ │ │ │ └── README.md +│ │ │ ├── conv +│ │ │ └── ... +│ │ └── formal/ +│ │ ├── common/ +│ │ │ ├── libs +│ │ │ └── Makefile +│ │ ├── add +│ │ ├── conv +│ │ └── ... +│ ├── code/ +│ │ ├── common/ +│ │ │ ├── drivers +│ │ │ ├── Makefile +│ │ │ └── libs +│ │ ├── add/ +│ │ │ ├── generated_code/ +│ │ │ │ ├── ocaml_code +│ │ │ │ └── c_code +│ │ │ ├── generated_doc +│ │ │ └── tests +│ │ ├── conv +│ │ └── ... +│ └── docs/ +│ ├── guidelines +│ └── tensor_lib + +└── graph +``` +## Main content +- [Informal specification](ops/spec/informal/) +- [Formal specification](ops/spec/formal/) +- [Guidelines](ops/docs/guidelines/) + - to [write informal specifications](./ops/docs/guidelines/informal.md) + - to write formal specifications (TBC) + - to write tests (TBC) + - [to manage the items's lifecycle](./ops/docs/guidelines/lifecycle.md) +- [Code and documentation generated from Why3](ops/code/) +- [Why3 installation guide](ops/docs/installation/) +- [Informal specification template](ops/spec/informal/common/template.md) +- [Tensor library formal specification](ops/spec/formal/common/libs/tensor/) +- [Tensor library documentation](ops/docs/tensor_lib/) diff --git a/safety-related-profile/meetings/reqs_sub_wg/minutes.md b/safety-related-profile/sonnx/graph/README.md similarity index 100% rename from safety-related-profile/meetings/reqs_sub_wg/minutes.md rename to safety-related-profile/sonnx/graph/README.md diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c new file mode 100644 index 00000000..ab6def31 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.c @@ -0,0 +1,15 @@ +#include "ctensoradd.h" + +void ctensor_add(struct ctensor a, struct ctensor b, struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = a.t_data[i] + b.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h new file mode 100644 index 00000000..aeeafc77 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.h @@ -0,0 +1,11 @@ +#ifndef CTENSORADD_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_add(struct ctensor a, struct ctensor b, struct ctensor r); + +#define CTENSORADD_H_INCLUDED +#endif // CTENSORADD_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o new file mode 100644 index 00000000..80a3eed9 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/add/generated_code/c_code/ctensoradd.o differ diff --git a/safety-related-profile/sonnx/ops/code/add/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/add/generated_code/ocaml_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/add/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/add/generated_doc/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c new file mode 100644 index 00000000..5e6f34d0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.c @@ -0,0 +1,20 @@ +#include "tensorclip.h" + +void ctensor_clip(struct ctensor x, struct ctensor l, struct ctensor m, + struct ctensor r) { + int32_t n, i, o; + double l_background, m_background; + n = cdim_size(r.t_dims, r.t_rank); + l_background = l.t_data[0]; + m_background = m.t_data[0]; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = infix_dtlsls(m_background, + infix_dtgtgt(x.t_data[i], l_background)); + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h new file mode 100644 index 00000000..f543106d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.h @@ -0,0 +1,12 @@ +#ifndef TENSORCLIP_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_clip(struct ctensor x, struct ctensor l, struct ctensor m, + struct ctensor r); + +#define TENSORCLIP_H_INCLUDED +#endif // TENSORCLIP_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o new file mode 100644 index 00000000..cc67f031 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/clip/generated_code/c_code/tensorclip.o differ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/README.md b/safety-related-profile/sonnx/ops/code/common/libs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o new file mode 100644 index 00000000..e7d84030 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..43e64d66 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff new file mode 100644 index 00000000..8f5e63c0 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff differ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2 b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2 new file mode 100644 index 00000000..f106190e Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/fonts/icofont.woff2 differ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css new file mode 100644 index 00000000..8402c60f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/icofont.min.css @@ -0,0 +1,7 @@ +/*! +* @package IcoFont +* @version 1.0.1 +* @author +* @copyright 2024 +* @license - https://icofont.com/license/ +*/@font-face{font-family:IcoFont;font-weight:400;font-style:Regular;src:url(fonts/icofont.woff2) format("woff2"),url(fonts/icofont.woff) format("woff")}[class*=" icofont-"],[class^=icofont-]{font-family:IcoFont!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;line-height:1;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased}.icofont-check:before{content:"\eed8"}.icofont-warning:before{content:"\f026"}.icofont-question-circle:before{content:"\efca"}.icofont-star:before{content:"\f000"}.icofont-error:before{content:"\ef16"}.icofont-warning-alt:before{content:"\f025"}[class*=" icofont-"].icofont-duotone,[class^=icofont-].icofont-duotone{position:relative}[class*=" icofont-"].icofont-duotone:before,[class^=icofont-].icofont-duotone:before{position:absolute;left:0;top:0}[class*=" icofont-"].icofont-duotone:after,[class^=icofont-].icofont-duotone:after{opacity:.4}.icofont-xs{font-size:.5em}.icofont-sm{font-size:.75em}.icofont-md{font-size:1.25em}.icofont-lg{font-size:1.5em}.icofont-1x{font-size:1em}.icofont-2x{font-size:2em}.icofont-3x{font-size:3em}.icofont-4x{font-size:4em}.icofont-5x{font-size:5em}.icofont-6x{font-size:6em}.icofont-7x{font-size:7em}.icofont-8x{font-size:8em}.icofont-9x{font-size:9em}.icofont-10x{font-size:10em}.icofont-fw{text-align:center;width:1.25em}.icofont-ul{list-style-type:none;padding-left:0;margin-left:0}.icofont-ul>li{position:relative;line-height:2em}.icofont-ul>li .icofont{display:inline-block;vertical-align:middle}.icofont-border{border:solid .08em #f1f1f1;border-radius:.1em;padding:.2em .25em .15em}.icofont-pull-left{float:left}.icofont-pull-right{float:right}.icofont.icofont-pull-left{margin-right:.3em}.icofont.icofont-pull-right{margin-left:.3em}.icofont-spin{-webkit-animation:icofont-spin 2s infinite linear;animation:icofont-spin 2s infinite linear;display:inline-block}.icofont-pulse{-webkit-animation:icofont-spin 1s infinite steps(8);animation:icofont-spin 1s infinite steps(8);display:inline-block}@-webkit-keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.icofont-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.icofont-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.icofont-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.icofont-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.icofont-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.icofont-flip-horizontal.icofont-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .icofont-flip-horizontal,:root .icofont-flip-vertical,:root .icofont-rotate-180,:root .icofont-rotate-270,:root .icofont-rotate-90{-webkit-filter:none;filter:none;display:inline-block}.icofont-inverse{color:#fff} \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html new file mode 100644 index 00000000..133d9f64 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/index.html @@ -0,0 +1,22 @@ + + + + + +Tensor Library + + +
index — Tensor Library
+

Development

+ + + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.CFlat.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.CFlat.html new file mode 100644 index 00000000..e73e5ba7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.CFlat.html @@ -0,0 +1,105 @@ + + + + + +Module layout.CFlat + + +
indexlibrary layoutmodule CFlat
+
+module CFlat
+  use tensor.std.Int
+  use tensor.std.List
+  use tensor.tensor.Range
+
+  function offset (ks ds : list int) : int =
+    match ks , ds with
+    | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+    | _ -> 0
+    end
+
+  function index (p : int) (ds : list int) : list int =
+    match ds with
+    | Nil -> Nil
+    | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+    end
+
+  let rec lemma offset_size (ks ds : list int)
+    requires { valid ks ds }
+    ensures { 0 <= offset ks ds < size ds }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons k krs , Cons d drs ->
+        offset_size krs drs ;
+        let p = offset ks ds in
+        let q = offset krs drs in
+        assert { p = size drs * k + q } ;
+        assert {
+             size drs * k
+          <= size drs * (d - 1)
+           = d * size drs - size drs
+        } ;
+      | _ -> ()
+      end
+    {qed}
+
+  let rec lemma index_range (p : int) (ds : list int)
+    requires { positive ds }
+    requires { 0 <= p < size ds }
+    ensures { valid (index p ds) ds }
+    {proof}
+    variant { ds }
+    = match ds with
+      | Nil -> ()
+      | Cons _ rds -> index_range (mod p (size rds)) rds
+      end
+    {qed}
+
+  let rec lemma index_of_offset (ks ds : list int)
+    requires { valid ks ds }
+    ensures { index (offset ks ds) ds = ks }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons k rks , Cons _ rds ->
+          index_of_offset rks rds ;
+          let p = offset ks ds in
+          let n = size rds in
+          let q = offset rks rds in
+          euclide p k n q ;
+      | _ -> ()
+      end
+    {qed}
+
+  let rec lemma offset_of_index (p : int) (ds : list int)
+    requires { positive ds }
+    requires { 0 <= p < size ds }
+    ensures { offset (index p ds) ds = p }
+    {proof}
+    variant { ds }
+    = match ds with
+      | Nil -> ()
+      | Cons _ rds ->
+        let n = size rds in
+        offset_of_index (mod p n) rds
+      end
+    {qed}
+
+  let rec lemma offset_push (ks ds : list int) (k d : int)
+    requires { valid ks ds }
+    ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+    {proof}
+    variant { ks }
+    = match ks , ds with
+      | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+      | _ -> ()
+      end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.index.html new file mode 100644 index 00000000..fb908df5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.index.html @@ -0,0 +1,13 @@ + + + + + +Library layout + + +
indexlibrary layout
+
module CFlat
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.proof.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.proof.html new file mode 100644 index 00000000..6ba4ddd9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/layout.proof.html @@ -0,0 +1,41 @@ + + + + + +Library layout + + +
indexlibrary layoutproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module layout.CFlat
+
+  goal offset_size
+    split_vc
+      alt-ergo 25ms
+      alt-ergo 13ms
+      alt-ergo 17ms
+      z3 13ms
+      cvc5 102ms
+  goal index_range
+    alt-ergo 101ms
+  goal index_of_offset
+    cvc5 72ms
+  goal offset_of_index
+    alt-ergo 189ms
+  goal offset_push
+    split_vc
+      alt-ergo 56ms
+      alt-ergo 15ms
+      split_vc
+        alt-ergo 1.4s
+        alt-ergo 17ms
+        alt-ergo 43ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.CTensor.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.CTensor.html new file mode 100644 index 00000000..745d8712 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.CTensor.html @@ -0,0 +1,130 @@ + + + + + +Module libtensor.CTensor + + +
indexlibrary libtensormodule CTensor
+
+module CTensor
+  use tensor.std.Int
+  use tensor.std.List
+  use tensor.std.Clib
+  use tensor.std.Cfloat
+  use mach.int.Int32
+  use tensor.tensor.Range
+  use tensor.tensor.Tensor
+  use tensor.layout.CFlat
+  use tensor.libvector.CIndex
+
+
+  type farray = ptr float
+
+  type ctensor = {
+    t_rank : int32 ;
+    t_dims : iarray ;
+    t_data : farray ;
+  }
+
+  function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+  function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+  predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+  predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+  predicate valid_tensor (t : ctensor) =
+    dimension t.t_dims t.t_rank /\
+    valid_range t.t_data 0 (tensor_size t) /\
+    writable t.t_data
+
+  function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+  function tensor_value_at (k : list int) (t : ctensor) : real =
+    if valid_index k t then
+      value_at t.t_data (tensor_offset k t)
+    else 0.0
+
+  function tensor_value (t : ctensor) : list int -> real =
+    fun k -> tensor_value_at k t
+
+  function tensor_valueb (t : ctensor) : list int -> bool =
+    fun k -> Real.(tensor_value_at k t > 0.0)
+
+  let ghost function tensor (t : ctensor) : tensor real
+    {proof}
+    requires { valid_tensor t }
+    ensures { result.dims = tensor_dim t }
+    ensures { result.data = tensor_value t }
+    ensures { result.background = 0.0 }
+    {qed}
+    = {
+      dims = pure { tensor_dim t } ;
+      data = pure { tensor_value t } ;
+      background = 0.0 ;
+    }
+
+  let ghost function tensorb (t : ctensor) : tensor bool
+    {proof}
+    requires { valid_tensor t }
+    ensures { result.dims = tensor_dim t }
+    ensures { result.data = tensor_valueb t }
+    ensures { result.background = false }
+    {qed}
+    = {
+      dims = pure { tensor_dim t } ;
+      data = pure { tensor_valueb t } ;
+      background = false ;
+    }
+
+  let ctensor_create (ds : iarray) (n : int32) : ctensor =
+    {proof}
+    requires { dimension ds n }
+    ensures { empty_tensor result \/ valid_tensor result }
+    ensures { empty_tensor result \/ result.t_rank = n }
+    ensures { empty_tensor result \/ result.t_dims = ds }
+    {qed}
+    let m = cdim_size ds n in
+    let vs = malloc (to_uint32 m) in
+    {
+      t_rank = if is_null vs then 0 else n ;
+      t_dims = ds ;
+      t_data = vs ;
+    }
+
+  let ctensor_clear (r : ctensor) =
+    requires { valid_tensor r }
+    ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+      {qed}
+      r.t_data[i] <- f32 0.0
+    done
+    {proof}
+    ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+    {qed}
+
+  let ctensor_reset (r : ctensor) (v : float) =
+    requires { valid_tensor r }
+    ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+      {qed}
+      r.t_data[i] <- v
+    done
+    {proof}
+    ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+    {qed}
+
+
+
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.index.html new file mode 100644 index 00000000..34b4db5e --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.index.html @@ -0,0 +1,13 @@ + + + + + +Library libtensor + + +
indexlibrary libtensor
+
module CTensor
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.proof.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.proof.html new file mode 100644 index 00000000..445f391e --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libtensor.proof.html @@ -0,0 +1,39 @@ + + + + + +Library libtensor + + +
indexlibrary libtensorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  215ms
+  cvc5         @1.2.1  n=42  489ms
+  z3           @4.13.4 n=30  140ms
+
+

Proofs

+
module libtensor.CTensor
+
+  goal tensor
+    alt-ergo 29ms
+  goal tensorb
+    alt-ergo 33ms
+  goal ctensor_create
+    alt-ergo 40ms
+  goal ctensor_clear
+    alt-ergo 242ms
+  goal ctensor_reset
+    split_vc
+      alt-ergo 20ms
+      alt-ergo 27ms
+      alt-ergo 20ms
+      alt-ergo 19ms
+      alt-ergo 34ms
+      alt-ergo 46ms
+      alt-ergo 75ms
+      alt-ergo 27ms
+      alt-ergo 34ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.CIndex.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.CIndex.html new file mode 100644 index 00000000..908f7e9a --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.CIndex.html @@ -0,0 +1,160 @@ + + + + + +Module libvector.CIndex + + + +
indexlibrary libvectormodule CIndex
+
+module CIndex
+  use tensor.std.Int
+  use tensor.std.List
+  use tensor.std.Clib
+  use mach.int.Int32
+  use tensor.tensor.Range
+  use tensor.layout.CFlat
+
+  type iarray = ptr int32
+
+  function ivector (u : iarray) (n : int) : list int
+    = map Int32.to_int (vector u n)
+
+  function islice (u : iarray) (p q : int) : list int
+    = map Int32.to_int (slice u p q)
+
+ +
+  function vdim (u : iarray) (n : int) : int = size (ivector u n)
+  function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+  predicate pdim (u : iarray) (p q : int) =
+    forall k. p <= k < q -> 0 < value_at u k
+
+  predicate dimension (u : iarray) (n : int) =
+    0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+
+

Equivalence betwen pdim and tensor.Range.positive.

+
+
+  let rec lemma positive_pdim (u : iarray) (p q : int)
+  {proof}
+    ensures { pdim u p q <-> positive (islice u p q) }
+    variant { q - p }
+    = if p < q then positive_pdim u (p+1) q
+  {qed}
+
+  let ghost sdim_split (u : iarray) (p k q : int)
+  {proof}
+    requires { p <= k < q }
+    requires { pdim u p q }
+    ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+    = assert { islice u p k ++ islice u k q = islice u p q }
+  {qed}
+
+
+

Extracted Library

+
+
+  let cdim_size (u : iarray) (n : int32): int32 =
+    ensures { result = vdim u n }
+    {proof}
+    requires { dimension u n }
+    {qed}
+    begin
+      let ref p = 1 in
+      for i = 0 to n - 1 do
+        {proof}
+        invariant { p = vdim u i }
+        ghost
+          begin
+            ensures { 1 <= p * u[i] <= max_int32 }
+            let ghost dl = pure { sdim u 0 i } in
+            let ghost di = Int32.to_int u[i] in
+            let ghost dr = pure { sdim u (i+1) n } in
+            let ghost dn = pure { sdim u 0 n } in
+            sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+            mult_bound (dl * di) dr dn ;
+          end ;
+        {qed}
+        p <- p * u[i] ;
+      done ; p
+    end
+
+  let cdim_create_1 (n : int32) : iarray =
+    requires { 0 < n }
+    ensures { is_not_null result -> vdim result 1 = n }
+    {proof}
+    ensures { is_not_null result -> dimension result 1 }
+    ensures { is_not_null result -> value_at result 0 = n }
+    {qed}
+    let cd = malloc 1 in
+    if is_not_null cd then cd[0] <- n ; cd
+
+  let cdim_create_2 (p q : int32) : iarray =
+    requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+    ensures { is_not_null result -> vdim result 2 = p * q }
+    {proof}
+    ensures { is_not_null result -> dimension result 2 }
+    ensures { is_not_null result -> value_at result 0 = p }
+    ensures { is_not_null result -> value_at result 1 = q }
+    {qed}
+    let cd = malloc 2 in
+    if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ +
+  let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+    {proof}
+    requires { 0 <= n }
+    requires { valid_range ks 0 n }
+    requires { dimension ds n }
+    ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+    ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+    {qed}
+    begin
+      let ref p = 0 in
+      for i = 0 to n - 1 do
+        {proof}
+        invariant { valid (ivector ks i) (ivector ds i) }
+        invariant { p = offset (ivector ks i) (ivector ds i) }
+        {qed}
+        let d = ds[i] in
+        let k = ks[i] in
+        if 0 <= k && k < d then
+          begin
+            {proof}
+            assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+            ghost
+              begin
+                ensures { Int32.in_bounds (p * d) }
+                ensures { Int32.in_bounds (p * d + k) }
+                let ghost dl = pure { sdim ds 0 i } in
+                let ghost di = Int32.to_int ds[i] in
+                let ghost dr = pure { sdim ds (i+1) n } in
+                let ghost dn = pure { sdim ds 0 n } in
+                sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+                mult_bound (dl * di) dr dn ;
+              end ;
+            {qed}
+            p <- p * d + k ;
+          end
+        else return (-1)
+      done ; p
+    end
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.index.html new file mode 100644 index 00000000..324ac9a4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.index.html @@ -0,0 +1,16 @@ + + + + + +Library libvector + + +
indexlibrary libvector
+
+

C–Library to compute Offsets & Dimensions

+
+
module CIndex
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.proof.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.proof.html new file mode 100644 index 00000000..76a8189b --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/libvector.proof.html @@ -0,0 +1,83 @@ + + + + + +Library libvector + + +
indexlibrary libvectorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module libvector.CIndex
+
+  goal positive_pdim
+    alt-ergo 69ms
+  goal sdim_split
+    alt-ergo 170ms
+  goal cdim_size
+    split_vc
+      alt-ergo 19ms
+      alt-ergo 22ms
+      alt-ergo 25ms
+      alt-ergo 17ms
+      alt-ergo 19ms
+      alt-ergo 35ms
+      alt-ergo 626ms
+      alt-ergo 24ms
+      alt-ergo 19ms
+      alt-ergo 126ms
+      alt-ergo 16ms
+      alt-ergo 14ms
+  goal cdim_create_1
+    alt-ergo 158ms
+  goal cdim_create_2
+    split_vc
+      alt-ergo 16ms
+      alt-ergo 13ms
+      alt-ergo 17ms
+      alt-ergo 17ms
+      alt-ergo 20ms
+      alt-ergo 262ms
+      alt-ergo 34ms
+      alt-ergo 23ms
+      alt-ergo 20ms
+      alt-ergo 16ms
+      alt-ergo 16ms
+      alt-ergo 15ms
+      alt-ergo 17ms
+  goal coffset
+    split_vc
+      alt-ergo 18ms
+      alt-ergo 24ms
+      alt-ergo 25ms
+      alt-ergo 24ms
+      alt-ergo 22ms
+      alt-ergo 835ms
+      alt-ergo 34ms
+      alt-ergo 18ms
+      alt-ergo 20ms
+      alt-ergo 65ms
+      alt-ergo 152ms
+      split_vc
+        inline_goal
+          split_vc
+            alt-ergo 28ms
+            stuck
+        alt-ergo 19ms
+      alt-ergo 18ms
+      alt-ergo 17ms
+      alt-ergo 902ms
+      alt-ergo 20ms
+      alt-ergo 17ms
+      alt-ergo 15ms
+      alt-ergo 18ms
+      alt-ergo 14ms
+      alt-ergo 16ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/script.js b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/script.js new file mode 100644 index 00000000..9e1faafc --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/script.js @@ -0,0 +1,54 @@ +function toggle(elt) { + var elts,j; + while (elt && !elt.classList.contains("section")) { + elt = elt.parentElement; + } + if (!elt) return; + elts = elt.querySelectorAll(".section-text, .section-toggle"); + for (j = 0; j < elts.length; j++) + elts[j].classList.toggle("active"); +} + +function focus() { + var h,elts,e,tk,i; + h = window.location.hash; + e = document.getElementById(h.substring(1)); + while (e) { + tk = e.classList; + if (tk.contains("section-text") && !tk.contains("active")) { + toggle(e); + break; + } + e = e.parentElement; + } +} + +function escape(evt) { + var elts,e,i; + if (evt.code === "Escape") { + window.location = ""; + elts = document.querySelectorAll(".section"); + for (i=0; i< elts.length; i++) { + e = elts[i]; + if (e.querySelectorAll(".section-toggle.active").length) + toggle(e); + } + } +} + +(function(){ + var nodes,i; + + nodes = document.getElementsByClassName("section-toggle"); + for (i = 0; i < nodes.length; i++) { + nodes[i].addEventListener("click", function() { + toggle(this); + }); + } + + focus(); + window.addEventListener('onload',focus); + window.addEventListener('hashchange',focus); + window.addEventListener('keypress',escape); + +})(); diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Cfloat.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Cfloat.html new file mode 100644 index 00000000..f92b0ba0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Cfloat.html @@ -0,0 +1,59 @@ + + + + + +Module std.Cfloat + + +
indexlibrary stdmodule Cfloat
+
+module Cfloat
+  use int.Int
+  use real.Real
+
+  type float = abstract { to_real : real }
+  meta coercion function to_real
+
+  type f32 = < float 8 24 >  (* single precision literals *)
+  type f64 = < float 11 53 > (* double precision literals *)
+
+  val function f32 (k : f32) : float
+    ensures { result = f32'real k }
+
+  val function f64 (k : f64) : float
+    ensures { result = f64'real k }
+
+  let constant zero = f64 0.0
+  let constant one  = f64 1.0
+
+  val function (.+) (a b : float) : float
+    ensures { Real.( result = a + b ) }
+
+  val function (.-) (a b : float) : float
+    ensures { Real.( result = a - b ) }
+
+  val function (.-_) (a : float) : float
+    ensures { Real.( result = (- a) ) }
+
+  val function ( .* ) (a b : float) : float
+    ensures { Real.( result = a * b ) }
+
+  val function ( ./ ) (a b : float) : float
+    requires { b <> 0.0 }
+    ensures { Real.( result = a / b ) }
+
+  val predicate ( .= ) (a b : float) : bool
+    ensures { result <-> Real.( a = b ) }
+
+  val predicate ( .< ) (a b : float) : bool
+    ensures { result <-> Real.( a < b ) }
+
+  val predicate ( .<= ) (a b : float) : bool
+    ensures { result <-> Real.( a <= b ) }
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Clib.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Clib.html new file mode 100644 index 00000000..a1288577 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Clib.html @@ -0,0 +1,117 @@ + + + + + +Module std.Clib + + +
indexlibrary stdmodule Clib
+
+module Clib
+  use map.Map
+  use int.Int
+  use mach.int.Int32
+  use export mach.c.C
+
+  type int32 = Int32.int32
+  type uint32 = UInt32.uint32
+
+  val function to_uint32 (v : int32) : uint32
+    requires { 0 <= v }
+    ensures { Int32.to_int v = UInt32.to_int result }
+
+
+

Null–Pointer

+
+
+  val predicate is_null (vp : ptr 'a) : bool
+    ensures { result <-> vp.zone = null_zone }
+
+
+

Array–range validity

+
+
+  predicate valid_range (vp : ptr 'a) (p q : int) =
+    q <= p \/
+    ( 0 <= vp.min <= vp.offset /\
+      0 <= p <= q <= max_int32 /\
+      0 <= vp.min <= vp.offset + p /\
+      vp.offset + q <= vp.max <= vp.plength )
+
+  let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+    {proof}
+    requires { p <= k < q }
+    requires { valid_range vp p q }
+    ensures { valid_ptr_shift vp k }
+    = ()
+    {qed}
+
+
+

Array as map

+
+
+  let ghost function value_at (vp : ptr 'a) : map int 'a =
+    pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+
+

Array access (ghost)

+
+
+  function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+
+

Array access (C, inlined)

+
+
+  let ([]) (vp : ptr 'a) (k : int32) : 'a
+    {proof}
+    requires { vp.min <= vp.offset + k < vp.max }
+    ensures { result = vp[k] }
+    = get_ofs vp k
+    {qed}
+
+
+

Array update (C, inlined)

+
+
+  let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+    requires { writable vp }
+    requires { vp.min <= vp.offset + k < vp.max }
+    ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+    = set_ofs vp k v
+
+
+

Array as list

+
+
+  use List
+
+  let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+    {proof}
+    variant { q - p }
+    {qed}
+    if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+  let rec lemma slice_append (u : ptr 'a) (p q r : int)
+    requires { p <= q <= r }
+    ensures { slice u p r = slice u p q ++ slice u q r }
+    {proof}
+    variant { q - p }
+    = if p < q then slice_append u (p+1) q r
+    {qed}
+
+  let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+  let lemma vector_push (u : ptr 'a) (n : int)
+    requires { 0 <= n }
+    ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+    {proof}
+    = slice_append u 0 n (n+1)
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Int.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Int.html new file mode 100644 index 00000000..3ded1908 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.Int.html @@ -0,0 +1,50 @@ + + + + + +Module std.Int + + +
indexlibrary stdmodule Int
+
+module Int
+  use export int.Int
+  use export int.Abs
+  use export int.ComputerDivision
+
+
+

Unicity of euclidian division decomposition

+
+
+  let ghost euclide (a b q r : int)
+    requires { 0 <= a /\ 0 <= r < q }
+    requires { a = b * q + r }
+    ensures { b = div a q }
+    ensures { r = mod a q }
+    {proof}
+    = let rec kernel (b r : int)
+        requires { b * q + r = 0 }
+        requires { abs r < q }
+        ensures { b = r = 0 }
+        variant { abs b }
+        = if b < 0 then kernel (b + 1) (r - q) else
+          if b > 0 then kernel (b - 1) (r + q) else ()
+      in kernel (b - div a q) (r - mod a q)
+    {qed}
+
+
+

Multiplication upper–bound

+
+
+  let ghost mult_bound (a b c : int)
+    requires { 0 < a * b <= c }
+    ensures { abs a <= c }
+    ensures { abs b <= c }
+    = ()
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.List.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.List.html new file mode 100644 index 00000000..13c07ac4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.List.html @@ -0,0 +1,34 @@ + + + + + +Module std.List + + +
indexlibrary stdmodule List
+
+module List
+  use export list.List
+  use export list.Map
+  use export list.Append
+
+
+

Appends an element to the end of the list

+
+
+  function push (xs : list 'a) (x : 'a) : list 'a =
+    xs ++ Cons x Nil
+
+  let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+    ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+    {proof}
+    variant { xs }
+    = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.index.html new file mode 100644 index 00000000..fe20174f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.index.html @@ -0,0 +1,16 @@ + + + + + +Library std + + +
indexlibrary std
+
module Int
+
module List
+
module Clib
+
module Cfloat
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.proof.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.proof.html new file mode 100644 index 00000000..ceb91d5d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/std.proof.html @@ -0,0 +1,63 @@ + + + + + +Library std + + +
indexlibrary stdproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module std.Int
+
+  goal euclide
+    alt-ergo 47ms
+  goal mult_bound
+    z3 6ms
module std.List
+
+  goal map_concat
+    alt-ergo 18ms
module std.Clib
+
+  value to_uint32
+  value is_null
+
+
+  goal valid_in_range
+    alt-ergo 14ms
+  goal ([])
+    alt-ergo 11ms
+  goal ([])
+    alt-ergo 11ms
+  goal ([]<-)
+    alt-ergo 15ms
+  goal slice
+    alt-ergo 14ms
+  goal slice_append
+    alt-ergo 29ms
+  goal vector_push
+    z3 16ms
module std.Cfloat
+
+  value f32
+  value f64
+  value (.+)
+  value (.-)
+  value (.-)
+  value (.*)
+  value (./)
+  value (.=)
+  value (.<)
+  value (.<=)
+
+
+  goal zero
+    split_vc
+  goal one
+    split_vc
+ + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/style.css b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/style.css new file mode 100644 index 00000000..1c6997e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/style.css @@ -0,0 +1,138 @@ +/* -------------------------------------------------------------------------- */ +/* --- CSS Style Sheet for why3find doc generator --- */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- Links --- */ +/* -------------------------------------------------------------------------- */ + +a { + color: teal; + outline: none; + text-decoration: none; +} + +a:visited { + color: teal; +} + +a:hover { + background: lightgreen; +} + +a:target { + background: lightblue; +} + +/* -------------------------------------------------------------------------- */ +/* --- Header Styling --- */ +/* -------------------------------------------------------------------------- */ + +header { + padding: 4px ; + color: darkgrey ; + border-bottom: thin solid darkgray ; +} + +header a { color: grey !important } + +/* -------------------------------------------------------------------------- */ +/* --- Table Of Contents --- */ +/* -------------------------------------------------------------------------- */ + +nav { display: none; } + +/* -------------------------------------------------------------------------- */ +/* --- Documentation Styling --- */ +/* -------------------------------------------------------------------------- */ + +div.doc { + /* Documentation block styling */ +} + +/* -------------------------------------------------------------------------- */ +/* --- Source Code Styling --- */ +/* -------------------------------------------------------------------------- */ + +pre.src { + /* Source code block styling */ +} + +.scope { + color: teal; +} + +.keyword { + color: purple; +} + +.admitted { + color: #DA2525; +} + +.attribute { + color: green; +} + +.comment { + color: grey; +} + +/* -------------------------------------------------------------------------- */ +/* --- Proof Marks --- */ +/* -------------------------------------------------------------------------- */ + +.icon { + margin-left: 2px ; + vertical-align: text-top ; + font-size: small ; + text-decoration: none ; +} + +.icon.small { font-size: x-small } + +.valid { color: green !important } +.failed { color: red !important } +.warning { color: orange !important } +.remark { color: lightgrey !important } +.prover:hover { color: purple } + +/* -------------------------------------------------------------------------- */ +/* --- Foldable Section --- */ +/* -------------------------------------------------------------------------- */ + +.section { } + +.section.level1:hover { + background: #fcfce1; +} + +.section.level2:hover { + background: #e9ffe9; +} + +.section.level3:hover { + background: #fff6f6; +} + +.section-toggle:hover { + background: lightgreen; +} + +.section-toggle { + cursor: zoom-in ; +} + +.section-toggle.active { + cursor: zoom-out ; +} + +.section-text { + display: none; +} + +.section-text.active { + display: inline; +} + +/* -------------------------------------------------------------------------- */ diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Range.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Range.html new file mode 100644 index 00000000..a2cbf7f7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Range.html @@ -0,0 +1,80 @@ + + + + + +Module tensor.Range + + +
indexlibrary tensormodule Range
+
+module Range
+  use int.Int
+  use tensor.std.List
+
+  function size (ds : list int) : int =
+    match ds with
+    | Nil -> 1
+    | Cons d ds -> d * size ds
+    end
+
+  predicate positive (ds : list int) =
+    match ds with
+    | Nil -> true
+    | Cons d ds -> 0 < d /\ positive ds
+    end
+
+  predicate valid (ks ds : list int) =
+    match ks , ds with
+    | Nil , Nil -> true
+    | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+    | _ -> false
+    end
+
+  let rec lemma valid_push (ks ds : list int) (k d : int)
+    requires { 0 <= k < d }
+    requires { valid ks ds }
+    ensures { valid (push ks k) (push ds d) }
+    {proof}
+    variant { ks }
+    = match ks , ds with
+      | Nil , Nil -> ()
+      | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+      | _ -> absurd
+      end
+    {qed}
+
+  let rec lemma size_append (xs ys : list int)
+    ensures { size (xs ++ ys) = size xs * size ys }
+    {proof}
+    variant { xs }
+    = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+    {qed}
+
+  lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+  let rec lemma positive_size (ds : list int)
+    requires { positive ds }
+    ensures { 0 < size ds }
+    {proof}
+    variant { ds }
+    = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+    {qed}
+
+  let rec lemma positive_valid (ks ds : list int)
+    requires { valid ks ds }
+    ensures { positive ds }
+    ensures { 0 < size ds }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons _ ks , Cons _ ds -> positive_valid ks ds
+      | _ -> ()
+      end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Tensor.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Tensor.html new file mode 100644 index 00000000..bc50c46d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.Tensor.html @@ -0,0 +1,112 @@ + + + + + +Module tensor.Tensor + + +
indexlibrary tensormodule Tensor
+
+
+

Formalization of Tensor

+
+
+module Tensor
+  use int.Int
+  use map.Map
+  use list.List
+  use Range
+
+  type data 'a = map (list int) 'a
+
+  type tensor 'a = {
+    dims : list int ;
+    data : data 'a ;
+    background : 'a ; (* default value, or value for 0-dimensions tensor *)
+  }
+  invariant { positive dims }
+  invariant { forall k. valid k dims \/ data k = background }
+  {proof}
+  by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+  {qed}
+
+  meta coercion function dims
+  meta coercion function data
+
+
+

Tensor with the same dimensions

+
+
+  predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+
+

Tensor with the same dimensions and background value

+
+
+  predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+    a ~ b /\ a.background = b.background
+
+
+

Equal tensors

+
+
+  predicate (==) (a b : tensor 'a) =
+    a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+
+

Extensionality

+
+
+  lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+  {proof} by tensor'eq a b {qed}
+
+
+

Scalar Tensor

+
+
+  let ghost function scalar (v : 'a) : tensor 'a
+    ensures { result.dims = Nil }
+    ensures { result.background = v }
+    ensures { forall k. result k = v }
+    {proof}
+    = { dims = Nil ; data = (fun _ -> v) ; background = v }
+    {qed}
+
+
+

Null Tensor

+
+
+  let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+    requires { positive ds }
+    ensures { result.dims = ds }
+    ensures { result.background = e }
+    ensures { forall k. result k = e }
+    {proof}
+    = { dims = ds ; data = (fun _ -> e) ; background = e }
+    {qed}
+
+
+

Constant Tensor

+
+
+  let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+    ensures { result.dims = ds }
+    requires { positive ds }
+    ensures { result.background = bg }
+    ensures { forall k. valid k ds -> result k = v }
+    {proof}
+    = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+    {qed}
+
+
+

Constant & Null

+
+
+  goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.index.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.index.html new file mode 100644 index 00000000..5626f691 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.index.html @@ -0,0 +1,18 @@ + + + + + +Library tensor + + +
indexlibrary tensor
+
+

Formalization of coordinates and dimensions

+
+
module Range
+
+
module Tensor
+ + + diff --git a/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.proof.html b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.proof.html new file mode 100644 index 00000000..0270105f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/common/libs/tensor/generated_doc/tensor.proof.html @@ -0,0 +1,46 @@ + + + + + +Library tensor + + +
indexlibrary tensorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  489ms
+  z3           @4.13.4 n=30  140ms
+
+

Proofs

+
module tensor.Range
+
+  goal valid_push
+    alt-ergo 32ms
+  goal size_append
+    alt-ergo 28ms
+  goal size_push
+    alt-ergo 11ms
+  goal positive_size
+    alt-ergo 18ms
+  goal positive_valid
+    alt-ergo 29ms
module tensor.Tensor
+
+  goal tensor
+    alt-ergo 12ms
+  goal exteq
+    split_vc
+      alt-ergo 13ms
+      alt-ergo 13ms
+      alt-ergo 13ms
+  goal scalar
+    alt-ergo 15ms
+  goal zero
+    alt-ergo 12ms
+  goal const
+    alt-ergo 17ms
+  goal zero_is_const
+    alt-ergo 12ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/concat/generated_code/c_code/README.md b/safety-related-profile/sonnx/ops/code/concat/generated_code/c_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/concat/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/concat/generated_code/ocaml_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/concat/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/concat/generated_doc/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/concat/tests/README.md b/safety-related-profile/sonnx/ops/code/concat/tests/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.c b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.c new file mode 100644 index 00000000..ff53bbee --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.c @@ -0,0 +1,284 @@ +#include "copconv2d.h" +struct __coords_from_X_p_result; + + +struct __coords_from_X_p_result coords_from_x_p(struct ctensor x, + int32_t * x_p_coords, + int32_t pad_top, + int32_t pad_left) { + int flag; + int32_t b, c, n, m, x_coords; + int32_t * x_coords_array; + struct __coords_from_X_p_result result, result1, result2; + flag = 0; + b = x_p_coords[0]; + c = x_p_coords[1]; + n = x_p_coords[2] - pad_top; + m = x_p_coords[3] - pad_left; + x_coords_array = malloc(((uint32_t) 4) * sizeof(int32_t)); + if (x_coords_array) { + flag = 1; + x_coords_array[0] = b; + x_coords_array[1] = c; + x_coords_array[2] = n; + x_coords_array[3] = m; + x_coords = coffset(x_coords_array, x.t_dims, x.t_rank); + if (x_coords >= 0) { + result.__field_0 = x.t_data[x_coords]; + result.__field_1 = flag; + return result; + } else { + result1.__field_0 = ((double) 0.0); + result1.__field_1 = flag; + return result1; + } + } else { + flag = 0; + result2.__field_0 = ((double) 0.0); + result2.__field_1 = flag; + return result2; + } +} +struct __w_cools_calculate_result; + + +struct __w_cools_calculate_result w_cools_calculate(struct ctensor x, + struct ctensor w, + int32_t c, int32_t i, + int32_t n, int32_t m, + int32_t y_h, int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left) { + int flag; + double sum; + int32_t * x_coords_array; + int32_t * w_coords_array; + int32_t cols, jj, o, x_h, x_w; + double x_val; + int aux_flag; + struct __coords_from_X_p_result struct_res; + int32_t w_coords; + double w_val; + struct __w_cools_calculate_result result, result1, result2; + flag = 0; + sum = ((double) 0.0); + x_coords_array = malloc(((uint32_t) 4) * sizeof(int32_t)); + w_coords_array = malloc(((uint32_t) 4) * sizeof(int32_t)); + cols = w.t_dims[3]; + if (x_coords_array && w_coords_array) { + flag = 1; + o = cols - 1; + if (0 <= o) { + for (jj = 0; ; ++jj) { + x_h = y_h * str_h + i * dil_h; + x_w = y_w * str_w + jj * dil_w; + x_coords_array[0] = n; + x_coords_array[1] = c; + x_coords_array[2] = x_h; + x_coords_array[3] = x_w; + struct_res = coords_from_x_p(x, x_coords_array, pad_top, pad_left); + x_val = struct_res.__field_0; + aux_flag = struct_res.__field_1; + if (aux_flag) { + w_coords_array[0] = m; + w_coords_array[1] = c; + w_coords_array[2] = i; + w_coords_array[3] = jj; + w_coords = coffset(w_coords_array, w.t_dims, w.t_rank); + w_val = w.t_data[w_coords]; + sum = sum + x_val * w_val; + } else { + flag = 0; + result.__field_0 = ((double) 0.0); + result.__field_1 = flag; + return result; + } + if (jj == o) { + break; + } + } + } + result1.__field_0 = sum; + result1.__field_1 = flag; + return result1; + } else { + flag = 0; + result2.__field_0 = ((double) 0.0); + result2.__field_1 = flag; + return result2; + } +} +struct __w_lines_calculate_result; + + +struct __w_lines_calculate_result w_lines_calculate(struct ctensor x, + struct ctensor w, + int32_t c, int32_t n, + int32_t m, int32_t y_h, + int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left) { + int32_t i; + int flag; + double sum; + int32_t ii, o; + double value; + int aux_flag; + struct __w_cools_calculate_result struct_res; + struct __w_lines_calculate_result result, result1; + i = w.t_dims[2]; + flag = 0; + sum = ((double) 0.0); + o = i - 1; + if (0 <= o) { + for (ii = 0; ; ++ii) { + struct_res = w_cools_calculate(x, w, c, ii, n, m, y_h, y_w, str_h, + str_w, dil_h, dil_w, pad_top, pad_left); + value = struct_res.__field_0; + aux_flag = struct_res.__field_1; + if (aux_flag) { + flag = 1; + sum = sum + value; + } else { + flag = 0; + result.__field_0 = ((double) 0.0); + result.__field_1 = flag; + return result; + } + if (ii == o) { + break; + } + } + } + result1.__field_0 = sum; + result1.__field_1 = flag; + return result1; +} +struct __w_channels_calculate_result; + + +struct __w_channels_calculate_result w_channels_calculate(struct ctensor x, + struct ctensor w, + int32_t n, + int32_t m, + int32_t y_h, + int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left) { + int32_t c; + int flag; + double sum; + int32_t cc, o; + double value; + int aux_flag; + struct __w_lines_calculate_result struct_res; + struct __w_channels_calculate_result result, result1; + c = w.t_dims[1]; + flag = 0; + sum = ((double) 0.0); + o = c - 1; + if (0 <= o) { + for (cc = 0; ; ++cc) { + struct_res = w_lines_calculate(x, w, cc, n, m, y_h, y_w, str_h, str_w, + dil_h, dil_w, pad_top, pad_left); + value = struct_res.__field_0; + aux_flag = struct_res.__field_1; + if (aux_flag) { + flag = 1; + sum = sum + value; + } else { + flag = 0; + result.__field_0 = ((double) 0.0); + result.__field_1 = flag; + return result; + } + if (cc == o) { + break; + } + } + } + result1.__field_0 = sum; + result1.__field_1 = flag; + return result1; +} + +int cconv(struct ctensor x, struct ctensor w, struct ctensor r, + int32_t str_h, int32_t str_w, int32_t dil_h, int32_t dil_w, + int32_t pad_top, int32_t pad_left, int32_t pad_bottom, + int32_t pad_right) { + int32_t n, m, y_h, y_w, nn, o, mm, o1, y_hh, o2, y_ww, o3; + int32_t * r_coords_array; + double value; + int flag; + struct __w_channels_calculate_result struct_res; + int32_t r_coords; + n = r.t_dims[0]; + m = r.t_dims[1]; + y_h = r.t_dims[2]; + y_w = r.t_dims[3]; + r_coords_array = malloc(((uint32_t) 4) * sizeof(int32_t)); + if (r_coords_array) { + o = n - 1; + if (0 <= o) { + for (nn = 0; ; ++nn) { + r_coords_array[0] = nn; + o1 = m - 1; + if (0 <= o1) { + for (mm = 0; ; ++mm) { + r_coords_array[1] = mm; + o2 = y_h - 1; + if (0 <= o2) { + for (y_hh = 0; ; ++y_hh) { + r_coords_array[2] = y_hh; + o3 = y_w - 1; + if (0 <= o3) { + for (y_ww = 0; ; ++y_ww) { + r_coords_array[3] = y_ww; + struct_res = w_channels_calculate(x, w, nn, mm, y_hh, + y_ww, str_h, str_w, dil_h, dil_w, pad_top, + pad_left); + value = struct_res.__field_0; + flag = struct_res.__field_1; + if (flag) { + r_coords = coffset(r_coords_array, r.t_dims, r.t_rank); + r.t_data[r_coords] = value; + } else { + return 0; + } + if (y_ww == o3) { + break; + } + } + } + if (y_hh == o2) { + break; + } + } + } + if (mm == o1) { + break; + } + } + } + if (nn == o) { + break; + } + } + } + return 1; + } else { + return 0; + } +} diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.h b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.h new file mode 100644 index 00000000..9b9846fb --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.h @@ -0,0 +1,76 @@ +#ifndef COPCONV2D_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +struct __coords_from_X_p_result { + double __field_0; + int __field_1; +}; + +struct __coords_from_X_p_result coords_from_x_p(struct ctensor x, + int32_t * x_p_coords, + int32_t pad_top, + int32_t pad_left); + +struct __w_cools_calculate_result { + double __field_0; + int __field_1; +}; + +struct __w_cools_calculate_result w_cools_calculate(struct ctensor x, + struct ctensor w, + int32_t c, int32_t i, + int32_t n, int32_t m, + int32_t y_h, int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left); + +struct __w_lines_calculate_result { + double __field_0; + int __field_1; +}; + +struct __w_lines_calculate_result w_lines_calculate(struct ctensor x, + struct ctensor w, + int32_t c, int32_t n, + int32_t m, int32_t y_h, + int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left); + +struct __w_channels_calculate_result { + double __field_0; + int __field_1; +}; + +struct __w_channels_calculate_result w_channels_calculate(struct ctensor x, + struct ctensor w, + int32_t n, + int32_t m, + int32_t y_h, + int32_t y_w, + int32_t str_h, + int32_t str_w, + int32_t dil_h, + int32_t dil_w, + int32_t pad_top, + int32_t pad_left); + +int cconv(struct ctensor x, struct ctensor w, struct ctensor r, + int32_t str_h, int32_t str_w, int32_t dil_h, int32_t dil_w, + int32_t pad_top, int32_t pad_left, int32_t pad_bottom, + int32_t pad_right); + +#define COPCONV2D_H_INCLUDED +#endif // COPCONV2D_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.o b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.o new file mode 100644 index 00000000..bc04b898 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/copconv2d.o differ diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/conv/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/conv/generated_code/ocaml_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/conv/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/conv/generated_doc/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/conv/tests/README.md b/safety-related-profile/sonnx/ops/code/conv/tests/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/div/generated_code/c_code/README.md b/safety-related-profile/sonnx/ops/code/div/generated_code/c_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/div/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/div/generated_code/ocaml_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/div/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/div/generated_doc/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/div/tests/README.md b/safety-related-profile/sonnx/ops/code/div/tests/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.c b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.c new file mode 100644 index 00000000..ace9b7c0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.c @@ -0,0 +1,15 @@ +#include "copflatten.h" + +void flatten(struct ctensor x, struct ctensor r, int32_t axis) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = x.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.h b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.h new file mode 100644 index 00000000..d80fb63e --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.h @@ -0,0 +1,11 @@ +#ifndef COPFLATTEN_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void flatten(struct ctensor x, struct ctensor r, int32_t axis); + +#define COPFLATTEN_H_INCLUDED +#endif // COPFLATTEN_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.o b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.o new file mode 100644 index 00000000..130eb973 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/copflatten.o differ diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/flatten/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.c b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.c new file mode 100644 index 00000000..60110f9f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.c @@ -0,0 +1,17 @@ +#include "ctensorleakyrelu.h" + +void ctensor_leaky_relu(double alpha, struct ctensor x, struct ctensor r) { + int32_t m, i, o; + double v; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + v = x.t_data[i]; + r.t_data[i] = ((double) 0.0) <= v ? v : alpha * v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.h b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.h new file mode 100644 index 00000000..a9c17815 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.h @@ -0,0 +1,11 @@ +#ifndef CTENSORLEAKYRELU_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_leaky_relu(double alpha, struct ctensor x, struct ctensor r); + +#define CTENSORLEAKYRELU_H_INCLUDED +#endif // CTENSORLEAKYRELU_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.o b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.o new file mode 100644 index 00000000..d46d07b6 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/leakyrelu/generated_code/c_code/ctensorleakyrelu.o differ diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.c b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.c new file mode 100644 index 00000000..3e138739 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.c @@ -0,0 +1,15 @@ +#include "ctensorless.h" + +void ctensor_less(struct ctensor x, struct ctensor y, struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = x.t_data[i] < y.t_data[i] ? ((double) 1.0) : ((double) 0.0); + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.h b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.h new file mode 100644 index 00000000..36478f09 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.h @@ -0,0 +1,11 @@ +#ifndef CTENSORLESS_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_less(struct ctensor x, struct ctensor y, struct ctensor r); + +#define CTENSORLESS_H_INCLUDED +#endif // CTENSORLESS_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.o b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.o new file mode 100644 index 00000000..294e3556 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/less/generated_code/c_code/ctensorless.o differ diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.c b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.c new file mode 100644 index 00000000..448c3619 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.c @@ -0,0 +1,64 @@ +#include "copmatmul.h" + +int matmul(struct ctensor a, struct ctensor b, struct ctensor r) { + int32_t rows, cols, iter; + int flag; + double sum; + int32_t * a_coords_array; + int32_t * b_coords_array; + int32_t * r_coords_array; + int32_t i, o, j, o1, k, o2, a_coords, b_coords, r_coords; + double a_val, b_val; + rows = a.t_dims[0]; + cols = b.t_dims[1]; + iter = a.t_dims[1]; + flag = 0; + sum = ((double) 0.0); + a_coords_array = malloc(((uint32_t) 2) * sizeof(int32_t)); + b_coords_array = malloc(((uint32_t) 2) * sizeof(int32_t)); + r_coords_array = malloc(((uint32_t) 2) * sizeof(int32_t)); + if (a_coords_array && (b_coords_array && r_coords_array)) { + flag = 1; + o = rows - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + o1 = cols - 1; + if (0 <= o1) { + for (j = 0; ; ++j) { + o2 = iter - 1; + if (0 <= o2) { + for (k = 0; ; ++k) { + a_coords_array[0] = i; + a_coords_array[1] = k; + b_coords_array[0] = k; + b_coords_array[1] = j; + a_coords = coffset(a_coords_array, a.t_dims, a.t_rank); + b_coords = coffset(b_coords_array, b.t_dims, b.t_rank); + a_val = a.t_data[a_coords]; + b_val = b.t_data[b_coords]; + sum = sum + a_val * b_val; + if (k == o2) { + break; + } + } + } + r_coords_array[0] = i; + r_coords_array[1] = j; + r_coords = coffset(r_coords_array, r.t_dims, r.t_rank); + r.t_data[r_coords] = sum; + sum = ((double) 0.0); + if (j == o1) { + break; + } + } + } + if (i == o) { + break; + } + } + } + return flag; + } else { + return flag; + } +} diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.h b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.h new file mode 100644 index 00000000..7c6d4639 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.h @@ -0,0 +1,11 @@ +#ifndef COPMATMUL_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +int matmul(struct ctensor a, struct ctensor b, struct ctensor r); + +#define COPMATMUL_H_INCLUDED +#endif // COPMATMUL_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.o b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.o new file mode 100644 index 00000000..898b51a2 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/copmatmul.o differ diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/matmul/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.c b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.c new file mode 100644 index 00000000..e8587619 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.c @@ -0,0 +1,15 @@ +#include "ctensormul.h" + +void ctensor_mul(struct ctensor a, struct ctensor b, struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = a.t_data[i] * b.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.h b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.h new file mode 100644 index 00000000..7cd5a728 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.h @@ -0,0 +1,11 @@ +#ifndef CTENSORMUL_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_mul(struct ctensor a, struct ctensor b, struct ctensor r); + +#define CTENSORMUL_H_INCLUDED +#endif // CTENSORMUL_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.o b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.o new file mode 100644 index 00000000..1599ba5c Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/mul/generated_code/c_code/ctensormul.o differ diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.c b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.c new file mode 100644 index 00000000..382d325d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.c @@ -0,0 +1,15 @@ +#include "ctensorrelu.h" + +void ctensor_relu(struct ctensor x, struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = x.t_data[i] < ((double) 0.0) ? ((double) 0.0) : x.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.h b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.h new file mode 100644 index 00000000..d26676f3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.h @@ -0,0 +1,11 @@ +#ifndef CTENSORRELU_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_relu(struct ctensor x, struct ctensor r); + +#define CTENSORRELU_H_INCLUDED +#endif // CTENSORRELU_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.o b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.o new file mode 100644 index 00000000..ee7046f5 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/relu/generated_code/c_code/ctensorrelu.o differ diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.c b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.c new file mode 100644 index 00000000..c4aa0871 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.c @@ -0,0 +1,127 @@ +#include "ctensorsoftmax.h" + +int same_dims(struct ctensor x, struct ctensor r) { + int ok; + int32_t i, o; + if (!(x.t_rank == r.t_rank)) { + return 0; + } else { + ok = 1; + o = x.t_rank - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + if (!(x.t_dims[i] == r.t_dims[i])) { + ok = 0; + } + if (i == o) { + break; + } + } + } + return ok; + } +} + +int32_t product_dims_range(int32_t * dims, int32_t first, int32_t last) { + int32_t p; + int32_t i, o; + p = 1; + o = last - 1; + if (first <= o) { + for (i = first; ; ++i) { + p = p * dims[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int c_softmax(struct ctensor x, struct ctensor r, int32_t axis) { + int32_t rank, axis_size, inner, outer, o, o1, inn, o2, base, a, o3, off; + double m, sum; + double v, shifted, e, shifted1, e1; + int32_t a1, o4, off1, a2, o5, off2; + if (x.t_rank <= 0) { + return 0; + } else { + if (axis < 0) { + return 0; + } else { + if (axis >= x.t_rank) { + return 0; + } else { + if (!same_dims(x, r)) { + return 0; + } else { + rank = x.t_rank; + axis_size = x.t_dims[axis]; + if (axis_size <= 0) { + return 0; + } else { + inner = product_dims_range(x.t_dims, axis + 1, rank); + outer = product_dims_range(x.t_dims, 0, axis); + o1 = outer - 1; + if (0 <= o1) { + for (o = 0; ; ++o) { + o2 = inner - 1; + if (0 <= o2) { + for (inn = 0; ; ++inn) { + base = o * axis_size * inner + inn; + m = x.t_data[base]; + o3 = axis_size - 1; + if (1 <= o3) { + for (a = 1; ; ++a) { + off = base + a * inner; + v = x.t_data[off]; + if (m < v) { + m = v; + } + if (a == o3) { + break; + } + } + } + sum = (0.0); + o4 = axis_size - 1; + if (0 <= o4) { + for (a1 = 0; ; ++a1) { + off1 = base + a1 * inner; + shifted = x.t_data[off1] - m; + e = (exp(shifted)); + sum = sum + e; + if (a1 == o4) { + break; + } + } + } + o5 = axis_size - 1; + if (0 <= o5) { + for (a2 = 0; ; ++a2) { + off2 = base + a2 * inner; + shifted1 = x.t_data[off2] - m; + e1 = (exp(shifted1)); + r.t_data[off2] = e1 / sum; + if (a2 == o5) { + break; + } + } + } + if (inn == o2) { + break; + } + } + } + if (o == o1) { + break; + } + } + } + return 1; + } + } + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.h b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.h new file mode 100644 index 00000000..0bf5accc --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.h @@ -0,0 +1,17 @@ +#ifndef CTENSORSOFTMAX_H_INCLUDED + +#include +#include +#include +#include +#include "cindex.h" +#include "ctensor.h" + +int same_dims(struct ctensor x, struct ctensor r); + +int32_t product_dims_range(int32_t * dims, int32_t first, int32_t last); + +int c_softmax(struct ctensor x, struct ctensor r, int32_t axis); + +#define CTENSORSOFTMAX_H_INCLUDED +#endif // CTENSORSOFTMAX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.o b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.o new file mode 100644 index 00000000..8065d40d Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/softmax/generated_code/c_code/ctensorsoftmax.o differ diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.c b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.c new file mode 100644 index 00000000..2ef3e800 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.c @@ -0,0 +1,15 @@ +#include "ctensortanh.h" + +void ctensor_tanh(struct ctensor a, struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ctanh(a.t_data[i]); + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.h b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.h new file mode 100644 index 00000000..c89c1ef4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.h @@ -0,0 +1,11 @@ +#ifndef CTENSORTANH_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_tanh(struct ctensor a, struct ctensor r); + +#define CTENSORTANH_H_INCLUDED +#endif // CTENSORTANH_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.o b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.o new file mode 100644 index 00000000..0d1334ec Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/tanh/generated_code/c_code/ctensortanh.o differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.c b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.c new file mode 100644 index 00000000..6d82f3e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.c @@ -0,0 +1,58 @@ +#include "cindex.h" + +int32_t cdim_size(int32_t * u, int32_t n) { + int32_t p; + int32_t i, o; + p = 1; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + p = p * u[i]; + if (i == o) { + break; + } + } + } + return p; +} + +int32_t * cdim_create_1(int32_t n) { + int32_t * cd; + cd = malloc(1U * sizeof(int32_t)); + if (cd) { + cd[0] = n; + } + return cd; +} + +int32_t * cdim_create_2(int32_t p, int32_t q) { + int32_t * cd; + cd = malloc(2U * sizeof(int32_t)); + if (cd) { + cd[0] = p; + cd[1] = q; + } + return cd; +} + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n) { + int32_t p; + int32_t i, o, d, k; + p = 0; + o = n - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + d = ds[i]; + k = ks[i]; + if (0 <= k && k < d) { + p = p * d + k; + } else { + return -1; + } + if (i == o) { + break; + } + } + } + return p; +} diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.h b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.h new file mode 100644 index 00000000..f8c9d87c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.h @@ -0,0 +1,15 @@ +#ifndef CINDEX_H_INCLUDED + +#include +#include + +int32_t cdim_size(int32_t * u, int32_t n); + +int32_t * cdim_create_1(int32_t n); + +int32_t * cdim_create_2(int32_t p, int32_t q); + +int32_t coffset(int32_t * ks, int32_t * ds, int32_t n); + +#define CINDEX_H_INCLUDED +#endif // CINDEX_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.o b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.o new file mode 100644 index 00000000..493a1869 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/cindex.o differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.c b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.c new file mode 100644 index 00000000..7c818502 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.c @@ -0,0 +1,43 @@ +#include "ctensor.h" +struct ctensor; + + +struct ctensor ctensor_create(int32_t * ds, int32_t n) { + int32_t m; + double * vs; + struct ctensor ctensor; + m = cdim_size(ds, n); + vs = malloc(((uint32_t) m) * sizeof(double)); + ctensor.t_rank = !vs ? 0 : n; + ctensor.t_dims = ds; + ctensor.t_data = vs; + return ctensor; +} + +void ctensor_clear(struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0); + if (i == o) { + break; + } + } + } +} + +void ctensor_reset(struct ctensor r, double v) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = v; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.h b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.h new file mode 100644 index 00000000..7aa119d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.h @@ -0,0 +1,20 @@ +#ifndef CTENSOR_H_INCLUDED + +#include +#include +#include "cindex.h" + +struct ctensor { + int32_t t_rank; + int32_t * t_dims; + double * t_data; +}; + +struct ctensor ctensor_create(int32_t * ds, int32_t n); + +void ctensor_clear(struct ctensor r); + +void ctensor_reset(struct ctensor r, double v); + +#define CTENSOR_H_INCLUDED +#endif // CTENSOR_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.o b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.o new file mode 100644 index 00000000..56705066 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensor.o differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.c b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.c new file mode 100644 index 00000000..de755b1b --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.c @@ -0,0 +1,16 @@ +#include "ctensorwhere.h" + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r) { + int32_t m, i, o; + m = cdim_size(r.t_dims, r.t_rank); + o = m - 1; + if (0 <= o) { + for (i = 0; ; ++i) { + r.t_data[i] = ((double) 0.0) < cond.t_data[i] ? a.t_data[i] : b.t_data[i]; + if (i == o) { + break; + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.h b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.h new file mode 100644 index 00000000..1c2509d0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.h @@ -0,0 +1,12 @@ +#ifndef CTENSORWHERE_H_INCLUDED + +#include +#include +#include "cindex.h" +#include "ctensor.h" + +void ctensor_where(struct ctensor cond, struct ctensor a, struct ctensor b, + struct ctensor r); + +#define CTENSORWHERE_H_INCLUDED +#endif // CTENSORWHERE_H_INCLUDED diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.o b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.o new file mode 100644 index 00000000..97748fdc Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/where/generated_code/c_code/ctensorwhere.o differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_code/ocaml_code/README.md b/safety-related-profile/sonnx/ops/code/where/generated_code/ocaml_code/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/README.md b/safety-related-profile/sonnx/ops/code/where/generated_doc/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff b/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff new file mode 100644 index 00000000..8f5e63c0 Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff2 b/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff2 new file mode 100644 index 00000000..f106190e Binary files /dev/null and b/safety-related-profile/sonnx/ops/code/where/generated_doc/fonts/icofont.woff2 differ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/icofont.min.css b/safety-related-profile/sonnx/ops/code/where/generated_doc/icofont.min.css new file mode 100644 index 00000000..8402c60f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/icofont.min.css @@ -0,0 +1,7 @@ +/*! +* @package IcoFont +* @version 1.0.1 +* @author +* @copyright 2024 +* @license - https://icofont.com/license/ +*/@font-face{font-family:IcoFont;font-weight:400;font-style:Regular;src:url(fonts/icofont.woff2) format("woff2"),url(fonts/icofont.woff) format("woff")}[class*=" icofont-"],[class^=icofont-]{font-family:IcoFont!important;speak:none;font-style:normal;font-weight:400;font-variant:normal;text-transform:none;white-space:nowrap;word-wrap:normal;direction:ltr;line-height:1;-webkit-font-feature-settings:"liga";-webkit-font-smoothing:antialiased}.icofont-check:before{content:"\eed8"}.icofont-warning:before{content:"\f026"}.icofont-question-circle:before{content:"\efca"}.icofont-star:before{content:"\f000"}.icofont-error:before{content:"\ef16"}.icofont-warning-alt:before{content:"\f025"}[class*=" icofont-"].icofont-duotone,[class^=icofont-].icofont-duotone{position:relative}[class*=" icofont-"].icofont-duotone:before,[class^=icofont-].icofont-duotone:before{position:absolute;left:0;top:0}[class*=" icofont-"].icofont-duotone:after,[class^=icofont-].icofont-duotone:after{opacity:.4}.icofont-xs{font-size:.5em}.icofont-sm{font-size:.75em}.icofont-md{font-size:1.25em}.icofont-lg{font-size:1.5em}.icofont-1x{font-size:1em}.icofont-2x{font-size:2em}.icofont-3x{font-size:3em}.icofont-4x{font-size:4em}.icofont-5x{font-size:5em}.icofont-6x{font-size:6em}.icofont-7x{font-size:7em}.icofont-8x{font-size:8em}.icofont-9x{font-size:9em}.icofont-10x{font-size:10em}.icofont-fw{text-align:center;width:1.25em}.icofont-ul{list-style-type:none;padding-left:0;margin-left:0}.icofont-ul>li{position:relative;line-height:2em}.icofont-ul>li .icofont{display:inline-block;vertical-align:middle}.icofont-border{border:solid .08em #f1f1f1;border-radius:.1em;padding:.2em .25em .15em}.icofont-pull-left{float:left}.icofont-pull-right{float:right}.icofont.icofont-pull-left{margin-right:.3em}.icofont.icofont-pull-right{margin-left:.3em}.icofont-spin{-webkit-animation:icofont-spin 2s infinite linear;animation:icofont-spin 2s infinite linear;display:inline-block}.icofont-pulse{-webkit-animation:icofont-spin 1s infinite steps(8);animation:icofont-spin 1s infinite steps(8);display:inline-block}@-webkit-keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}@keyframes icofont-spin{0%{-webkit-transform:rotate(0);transform:rotate(0)}100%{-webkit-transform:rotate(360deg);transform:rotate(360deg)}}.icofont-rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.icofont-rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.icofont-rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.icofont-flip-horizontal{-webkit-transform:scale(-1,1);transform:scale(-1,1)}.icofont-flip-vertical{-webkit-transform:scale(1,-1);transform:scale(1,-1)}.icofont-flip-horizontal.icofont-flip-vertical{-webkit-transform:scale(-1,-1);transform:scale(-1,-1)}:root .icofont-flip-horizontal,:root .icofont-flip-vertical,:root .icofont-rotate-180,:root .icofont-rotate-270,:root .icofont-rotate-90{-webkit-filter:none;filter:none;display:inline-block}.icofont-inverse{color:#fff} \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/index.html new file mode 100644 index 00000000..90572cc0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/index.html @@ -0,0 +1,18 @@ + + + + + +Where operator + + +
index — Where operator
+

Development

+ + + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.CFlat.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.CFlat.html new file mode 100644 index 00000000..4ba4086d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.CFlat.html @@ -0,0 +1,105 @@ + + + + + +Module layout.CFlat + + +
indexlibrary layoutmodule CFlat
+
+module CFlat
+  use std.Int
+  use std.List
+  use tensor.Range
+
+  function offset (ks ds : list int) : int =
+    match ks , ds with
+    | Cons k ks , Cons _ ds -> k * size ds + offset ks ds
+    | _ -> 0
+    end
+
+  function index (p : int) (ds : list int) : list int =
+    match ds with
+    | Nil -> Nil
+    | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds)
+    end
+
+  let rec lemma offset_size (ks ds : list int)
+    requires { valid ks ds }
+    ensures { 0 <= offset ks ds < size ds }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons k krs , Cons d drs ->
+        offset_size krs drs ;
+        let p = offset ks ds in
+        let q = offset krs drs in
+        assert { p = size drs * k + q } ;
+        assert {
+             size drs * k
+          <= size drs * (d - 1)
+           = d * size drs - size drs
+        } ;
+      | _ -> ()
+      end
+    {qed}
+
+  let rec lemma index_range (p : int) (ds : list int)
+    requires { positive ds }
+    requires { 0 <= p < size ds }
+    ensures { valid (index p ds) ds }
+    {proof}
+    variant { ds }
+    = match ds with
+      | Nil -> ()
+      | Cons _ rds -> index_range (mod p (size rds)) rds
+      end
+    {qed}
+
+  let rec lemma index_of_offset (ks ds : list int)
+    requires { valid ks ds }
+    ensures { index (offset ks ds) ds = ks }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons k rks , Cons _ rds ->
+          index_of_offset rks rds ;
+          let p = offset ks ds in
+          let n = size rds in
+          let q = offset rks rds in
+          euclide p k n q ;
+      | _ -> ()
+      end
+    {qed}
+
+  let rec lemma offset_of_index (p : int) (ds : list int)
+    requires { positive ds }
+    requires { 0 <= p < size ds }
+    ensures { offset (index p ds) ds = p }
+    {proof}
+    variant { ds }
+    = match ds with
+      | Nil -> ()
+      | Cons _ rds ->
+        let n = size rds in
+        offset_of_index (mod p n) rds
+      end
+    {qed}
+
+  let rec lemma offset_push (ks ds : list int) (k d : int)
+    requires { valid ks ds }
+    ensures { offset (push ks k) (push ds d) = offset ks ds * d + k }
+    {proof}
+    variant { ks }
+    = match ks , ds with
+      | Cons _ krs , Cons _ drs -> offset_push krs drs k d
+      | _ -> ()
+      end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.index.html new file mode 100644 index 00000000..fb908df5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.index.html @@ -0,0 +1,13 @@ + + + + + +Library layout + + +
indexlibrary layout
+
module CFlat
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.proof.html new file mode 100644 index 00000000..6ba4ddd9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/layout.proof.html @@ -0,0 +1,41 @@ + + + + + +Library layout + + +
indexlibrary layoutproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module layout.CFlat
+
+  goal offset_size
+    split_vc
+      alt-ergo 25ms
+      alt-ergo 13ms
+      alt-ergo 17ms
+      z3 13ms
+      cvc5 102ms
+  goal index_range
+    alt-ergo 101ms
+  goal index_of_offset
+    cvc5 72ms
+  goal offset_of_index
+    alt-ergo 189ms
+  goal offset_push
+    split_vc
+      alt-ergo 56ms
+      alt-ergo 15ms
+      split_vc
+        alt-ergo 1.4s
+        alt-ergo 17ms
+        alt-ergo 43ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.CTensor.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.CTensor.html new file mode 100644 index 00000000..0236b4a6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.CTensor.html @@ -0,0 +1,154 @@ + + + + + +Module libtensor.CTensor + + +
indexlibrary libtensormodule CTensor
+
+module CTensor
+  use std.Int
+  use std.List
+  use std.Clib
+  use std.Cfloat
+  use mach.int.Int32
+  use tensor.Range
+  use tensor.Tensor
+  use tensor.OPWhere
+  use layout.CFlat
+  use libvector.CIndex
+
+  type farray = ptr float
+
+  type ctensor = {
+    t_rank : int32 ;
+    t_dims : iarray ;
+    t_data : farray ;
+  }
+
+  function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank
+  function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank
+  predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t)
+  predicate empty_tensor (t : ctensor) = t.t_rank = 0
+
+  predicate valid_tensor (t : ctensor) =
+    dimension t.t_dims t.t_rank /\
+    valid_range t.t_data 0 (tensor_size t) /\
+    writable t.t_data
+
+  function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t)
+
+  function tensor_value_at (k : list int) (t : ctensor) : real =
+    if valid_index k t then
+      value_at t.t_data (tensor_offset k t)
+    else 0.0
+
+  function tensor_value (t : ctensor) : list int -> real =
+    fun k -> tensor_value_at k t
+
+  function tensor_valueb (t : ctensor) : list int -> bool =
+    fun k -> Real.(tensor_value_at k t > 0.0)
+
+  let ghost function tensor (t : ctensor) : tensor real
+    {proof}
+    requires { valid_tensor t }
+    ensures { result.dims = tensor_dim t }
+    ensures { result.data = tensor_value t }
+    ensures { result.background = 0.0 }
+    {qed}
+    = {
+      dims = pure { tensor_dim t } ;
+      data = pure { tensor_value t } ;
+      background = 0.0 ;
+    }
+
+  let ghost function tensorb (t : ctensor) : tensor bool
+    {proof}
+    requires { valid_tensor t }
+    ensures { result.dims = tensor_dim t }
+    ensures { result.data = tensor_valueb t }
+    ensures { result.background = false }
+    {qed}
+    = {
+      dims = pure { tensor_dim t } ;
+      data = pure { tensor_valueb t } ;
+      background = false ;
+    }
+
+  let ctensor_create (ds : iarray) (n : int32) : ctensor =
+    {proof}
+    requires { dimension ds n }
+    ensures { empty_tensor result \/ valid_tensor result }
+    ensures { empty_tensor result \/ result.t_rank = n }
+    ensures { empty_tensor result \/ result.t_dims = ds }
+    {qed}
+    let m = cdim_size ds n in
+    let vs = malloc (to_uint32 m) in
+    {
+      t_rank = if is_null vs then 0 else n ;
+      t_dims = ds ;
+      t_data = vs ;
+    }
+
+  let ctensor_clear (r : ctensor) =
+    requires { valid_tensor r }
+    ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 }
+      {qed}
+      r.t_data[i] <- f32 0.0
+    done
+    {proof}
+    ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) }
+    {qed}
+
+  let ctensor_reset (r : ctensor) (v : float) =
+    requires { valid_tensor r }
+    ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant { forall k. 0 <= k < i -> value_at r.t_data k = v }
+      {qed}
+      r.t_data[i] <- v
+    done
+    {proof}
+    ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) }
+    {qed}
+
+  let ctensor_where (cond a b r : ctensor) =
+    {proof}
+    requires { valid_tensor cond }
+    requires { valid_tensor a }
+    requires { valid_tensor b }
+    requires { valid_tensor r }
+    requires { tensor a ~= tensor b ~= tensor r }
+    {qed}
+    requires { tensorb cond ~ tensor a ~ tensor b }
+    ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant {
+        forall k. 0 <= k < i ->
+          value_at r.t_data k =
+            if Real.(0.0 < value_at cond.t_data k)
+            then a.t_data[k] else b.t_data[k]
+      }
+      {qed}
+      r.t_data[i] <-
+        if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+    done
+    {proof}
+    ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.index.html new file mode 100644 index 00000000..abc11ecc --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.index.html @@ -0,0 +1,13 @@ + + + + + +Library libtensor + + +
indexlibrary libtensor
+
module CTensor
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.proof.html new file mode 100644 index 00000000..faf1a5c2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libtensor.proof.html @@ -0,0 +1,67 @@ + + + + + +Library libtensor + + +
indexlibrary libtensorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  215ms
+  cvc5         @1.2.1  n=42  489ms
+  z3           @4.13.4 n=30  140ms
+
+

Proofs

+
module libtensor.CTensor
+
+  value to_uint32
+  value is_null
+  value f32
+  value f64
+  value (.+)
+  value (.-)
+  value (.-)
+  value (.*)
+  value (./)
+  value (.=)
+  value (.<)
+  value (.<=)
+
+
+  goal tensor
+    alt-ergo 29ms
+  goal tensorb
+    alt-ergo 33ms
+  goal ctensor_create
+    alt-ergo 40ms
+  goal ctensor_clear
+    alt-ergo 242ms
+  goal ctensor_reset
+    split_vc
+      alt-ergo 20ms
+      alt-ergo 27ms
+      alt-ergo 20ms
+      alt-ergo 19ms
+      alt-ergo 34ms
+      alt-ergo 46ms
+      alt-ergo 75ms
+      alt-ergo 27ms
+      alt-ergo 34ms
+  goal ctensor_where
+    split_vc
+      alt-ergo 24ms
+      alt-ergo 34ms
+      alt-ergo 24ms
+      alt-ergo 80ms
+      alt-ergo 81ms
+      alt-ergo 84ms
+      alt-ergo 25ms
+      alt-ergo 47ms
+      alt-ergo 82ms
+      alt-ergo 301ms
+      alt-ergo 28ms
+      alt-ergo 33ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.CIndex.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.CIndex.html new file mode 100644 index 00000000..f0a09860 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.CIndex.html @@ -0,0 +1,160 @@ + + + + + +Module libvector.CIndex + + + +
indexlibrary libvectormodule CIndex
+
+module CIndex
+  use std.Int
+  use std.List
+  use std.Clib
+  use mach.int.Int32
+  use tensor.Range
+  use layout.CFlat
+
+  type iarray = ptr int32
+
+  function ivector (u : iarray) (n : int) : list int
+    = map Int32.to_int (vector u n)
+
+  function islice (u : iarray) (p q : int) : list int
+    = map Int32.to_int (slice u p q)
+
+ +
+  function vdim (u : iarray) (n : int) : int = size (ivector u n)
+  function sdim (u : iarray) (p q : int) : int = size (islice u p q)
+
+  predicate pdim (u : iarray) (p q : int) =
+    forall k. p <= k < q -> 0 < value_at u k
+
+  predicate dimension (u : iarray) (n : int) =
+    0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32
+
+
+

Equivalence betwen pdim and tensor.Range.positive.

+
+
+  let rec lemma positive_pdim (u : iarray) (p q : int)
+  {proof}
+    ensures { pdim u p q <-> positive (islice u p q) }
+    variant { q - p }
+    = if p < q then positive_pdim u (p+1) q
+  {qed}
+
+  let ghost sdim_split (u : iarray) (p k q : int)
+  {proof}
+    requires { p <= k < q }
+    requires { pdim u p q }
+    ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q }
+    = assert { islice u p k ++ islice u k q = islice u p q }
+  {qed}
+
+
+

Extracted Library

+
+
+  let cdim_size (u : iarray) (n : int32): int32 =
+    ensures { result = vdim u n }
+    {proof}
+    requires { dimension u n }
+    {qed}
+    begin
+      let ref p = 1 in
+      for i = 0 to n - 1 do
+        {proof}
+        invariant { p = vdim u i }
+        ghost
+          begin
+            ensures { 1 <= p * u[i] <= max_int32 }
+            let ghost dl = pure { sdim u 0 i } in
+            let ghost di = Int32.to_int u[i] in
+            let ghost dr = pure { sdim u (i+1) n } in
+            let ghost dn = pure { sdim u 0 n } in
+            sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ;
+            mult_bound (dl * di) dr dn ;
+          end ;
+        {qed}
+        p <- p * u[i] ;
+      done ; p
+    end
+
+  let cdim_create_1 (n : int32) : iarray =
+    requires { 0 < n }
+    ensures { is_not_null result -> vdim result 1 = n }
+    {proof}
+    ensures { is_not_null result -> dimension result 1 }
+    ensures { is_not_null result -> value_at result 0 = n }
+    {qed}
+    let cd = malloc 1 in
+    if is_not_null cd then cd[0] <- n ; cd
+
+  let cdim_create_2 (p q : int32) : iarray =
+    requires { 0 < p /\ 0 < q /\ p * q <= max_int32 }
+    ensures { is_not_null result -> vdim result 2 = p * q }
+    {proof}
+    ensures { is_not_null result -> dimension result 2 }
+    ensures { is_not_null result -> value_at result 0 = p }
+    ensures { is_not_null result -> value_at result 1 = q }
+    {qed}
+    let cd = malloc 2 in
+    if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd
+
+ +
+  let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 =
+    {proof}
+    requires { 0 <= n }
+    requires { valid_range ks 0 n }
+    requires { dimension ds n }
+    ensures { 0 <= result -> valid (ivector ks n) (ivector ds n) }
+    ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) }
+    {qed}
+    begin
+      let ref p = 0 in
+      for i = 0 to n - 1 do
+        {proof}
+        invariant { valid (ivector ks i) (ivector ds i) }
+        invariant { p = offset (ivector ks i) (ivector ds i) }
+        {qed}
+        let d = ds[i] in
+        let k = ks[i] in
+        if 0 <= k && k < d then
+          begin
+            {proof}
+            assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ;
+            ghost
+              begin
+                ensures { Int32.in_bounds (p * d) }
+                ensures { Int32.in_bounds (p * d + k) }
+                let ghost dl = pure { sdim ds 0 i } in
+                let ghost di = Int32.to_int ds[i] in
+                let ghost dr = pure { sdim ds (i+1) n } in
+                let ghost dn = pure { sdim ds 0 n } in
+                sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ;
+                mult_bound (dl * di) dr dn ;
+              end ;
+            {qed}
+            p <- p * d + k ;
+          end
+        else return (-1)
+      done ; p
+    end
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.index.html new file mode 100644 index 00000000..324ac9a4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.index.html @@ -0,0 +1,16 @@ + + + + + +Library libvector + + +
indexlibrary libvector
+
+

C–Library to compute Offsets & Dimensions

+
+
module CIndex
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.proof.html new file mode 100644 index 00000000..a1a27bc7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/libvector.proof.html @@ -0,0 +1,87 @@ + + + + + +Library libvector + + +
indexlibrary libvectorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module libvector.CIndex
+
+  value to_uint32
+  value is_null
+
+
+  goal positive_pdim
+    alt-ergo 69ms
+  goal sdim_split
+    alt-ergo 170ms
+  goal cdim_size
+    split_vc
+      alt-ergo 19ms
+      alt-ergo 22ms
+      alt-ergo 25ms
+      alt-ergo 17ms
+      alt-ergo 19ms
+      alt-ergo 35ms
+      alt-ergo 626ms
+      alt-ergo 24ms
+      alt-ergo 19ms
+      alt-ergo 126ms
+      alt-ergo 16ms
+      alt-ergo 14ms
+  goal cdim_create_1
+    alt-ergo 158ms
+  goal cdim_create_2
+    split_vc
+      alt-ergo 16ms
+      alt-ergo 13ms
+      alt-ergo 17ms
+      alt-ergo 17ms
+      alt-ergo 20ms
+      alt-ergo 262ms
+      alt-ergo 34ms
+      alt-ergo 23ms
+      alt-ergo 20ms
+      alt-ergo 16ms
+      alt-ergo 16ms
+      alt-ergo 15ms
+      alt-ergo 17ms
+  goal coffset
+    split_vc
+      alt-ergo 18ms
+      alt-ergo 24ms
+      alt-ergo 25ms
+      alt-ergo 24ms
+      alt-ergo 22ms
+      alt-ergo 835ms
+      alt-ergo 34ms
+      alt-ergo 18ms
+      alt-ergo 20ms
+      alt-ergo 65ms
+      alt-ergo 152ms
+      split_vc
+        inline_goal
+          split_vc
+            alt-ergo 28ms
+            stuck
+        alt-ergo 19ms
+      alt-ergo 18ms
+      alt-ergo 17ms
+      alt-ergo 902ms
+      alt-ergo 20ms
+      alt-ergo 17ms
+      alt-ergo 15ms
+      alt-ergo 18ms
+      alt-ergo 14ms
+      alt-ergo 16ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/script.js b/safety-related-profile/sonnx/ops/code/where/generated_doc/script.js new file mode 100644 index 00000000..9e1faafc --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/script.js @@ -0,0 +1,54 @@ +function toggle(elt) { + var elts,j; + while (elt && !elt.classList.contains("section")) { + elt = elt.parentElement; + } + if (!elt) return; + elts = elt.querySelectorAll(".section-text, .section-toggle"); + for (j = 0; j < elts.length; j++) + elts[j].classList.toggle("active"); +} + +function focus() { + var h,elts,e,tk,i; + h = window.location.hash; + e = document.getElementById(h.substring(1)); + while (e) { + tk = e.classList; + if (tk.contains("section-text") && !tk.contains("active")) { + toggle(e); + break; + } + e = e.parentElement; + } +} + +function escape(evt) { + var elts,e,i; + if (evt.code === "Escape") { + window.location = ""; + elts = document.querySelectorAll(".section"); + for (i=0; i< elts.length; i++) { + e = elts[i]; + if (e.querySelectorAll(".section-toggle.active").length) + toggle(e); + } + } +} + +(function(){ + var nodes,i; + + nodes = document.getElementsByClassName("section-toggle"); + for (i = 0; i < nodes.length; i++) { + nodes[i].addEventListener("click", function() { + toggle(this); + }); + } + + focus(); + window.addEventListener('onload',focus); + window.addEventListener('hashchange',focus); + window.addEventListener('keypress',escape); + +})(); diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Cfloat.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Cfloat.html new file mode 100644 index 00000000..f92b0ba0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Cfloat.html @@ -0,0 +1,59 @@ + + + + + +Module std.Cfloat + + +
indexlibrary stdmodule Cfloat
+
+module Cfloat
+  use int.Int
+  use real.Real
+
+  type float = abstract { to_real : real }
+  meta coercion function to_real
+
+  type f32 = < float 8 24 >  (* single precision literals *)
+  type f64 = < float 11 53 > (* double precision literals *)
+
+  val function f32 (k : f32) : float
+    ensures { result = f32'real k }
+
+  val function f64 (k : f64) : float
+    ensures { result = f64'real k }
+
+  let constant zero = f64 0.0
+  let constant one  = f64 1.0
+
+  val function (.+) (a b : float) : float
+    ensures { Real.( result = a + b ) }
+
+  val function (.-) (a b : float) : float
+    ensures { Real.( result = a - b ) }
+
+  val function (.-_) (a : float) : float
+    ensures { Real.( result = (- a) ) }
+
+  val function ( .* ) (a b : float) : float
+    ensures { Real.( result = a * b ) }
+
+  val function ( ./ ) (a b : float) : float
+    requires { b <> 0.0 }
+    ensures { Real.( result = a / b ) }
+
+  val predicate ( .= ) (a b : float) : bool
+    ensures { result <-> Real.( a = b ) }
+
+  val predicate ( .< ) (a b : float) : bool
+    ensures { result <-> Real.( a < b ) }
+
+  val predicate ( .<= ) (a b : float) : bool
+    ensures { result <-> Real.( a <= b ) }
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Clib.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Clib.html new file mode 100644 index 00000000..a1288577 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Clib.html @@ -0,0 +1,117 @@ + + + + + +Module std.Clib + + +
indexlibrary stdmodule Clib
+
+module Clib
+  use map.Map
+  use int.Int
+  use mach.int.Int32
+  use export mach.c.C
+
+  type int32 = Int32.int32
+  type uint32 = UInt32.uint32
+
+  val function to_uint32 (v : int32) : uint32
+    requires { 0 <= v }
+    ensures { Int32.to_int v = UInt32.to_int result }
+
+
+

Null–Pointer

+
+
+  val predicate is_null (vp : ptr 'a) : bool
+    ensures { result <-> vp.zone = null_zone }
+
+
+

Array–range validity

+
+
+  predicate valid_range (vp : ptr 'a) (p q : int) =
+    q <= p \/
+    ( 0 <= vp.min <= vp.offset /\
+      0 <= p <= q <= max_int32 /\
+      0 <= vp.min <= vp.offset + p /\
+      vp.offset + q <= vp.max <= vp.plength )
+
+  let lemma valid_in_range (vp : ptr 'a) (p k q : int)
+    {proof}
+    requires { p <= k < q }
+    requires { valid_range vp p q }
+    ensures { valid_ptr_shift vp k }
+    = ()
+    {qed}
+
+
+

Array as map

+
+
+  let ghost function value_at (vp : ptr 'a) : map int 'a =
+    pure { fun k -> vp.data.Array.elts (vp.offset + k) }
+
+
+

Array access (ghost)

+
+
+  function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k
+
+
+

Array access (C, inlined)

+
+
+  let ([]) (vp : ptr 'a) (k : int32) : 'a
+    {proof}
+    requires { vp.min <= vp.offset + k < vp.max }
+    ensures { result = vp[k] }
+    = get_ofs vp k
+    {qed}
+
+
+

Array update (C, inlined)

+
+
+  let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit
+    requires { writable vp }
+    requires { vp.min <= vp.offset + k < vp.max }
+    ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v }
+    = set_ofs vp k v
+
+
+

Array as list

+
+
+  use List
+
+  let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a =
+    {proof}
+    variant { q - p }
+    {qed}
+    if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q)
+
+  let rec lemma slice_append (u : ptr 'a) (p q r : int)
+    requires { p <= q <= r }
+    ensures { slice u p r = slice u p q ++ slice u q r }
+    {proof}
+    variant { q - p }
+    = if p < q then slice_append u (p+1) q r
+    {qed}
+
+  let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n
+
+  let lemma vector_push (u : ptr 'a) (n : int)
+    requires { 0 <= n }
+    ensures { vector u (n+1) = push (vector u n) (value_at u n) }
+    {proof}
+    = slice_append u 0 n (n+1)
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Int.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Int.html new file mode 100644 index 00000000..3ded1908 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.Int.html @@ -0,0 +1,50 @@ + + + + + +Module std.Int + + +
indexlibrary stdmodule Int
+
+module Int
+  use export int.Int
+  use export int.Abs
+  use export int.ComputerDivision
+
+
+

Unicity of euclidian division decomposition

+
+
+  let ghost euclide (a b q r : int)
+    requires { 0 <= a /\ 0 <= r < q }
+    requires { a = b * q + r }
+    ensures { b = div a q }
+    ensures { r = mod a q }
+    {proof}
+    = let rec kernel (b r : int)
+        requires { b * q + r = 0 }
+        requires { abs r < q }
+        ensures { b = r = 0 }
+        variant { abs b }
+        = if b < 0 then kernel (b + 1) (r - q) else
+          if b > 0 then kernel (b - 1) (r + q) else ()
+      in kernel (b - div a q) (r - mod a q)
+    {qed}
+
+
+

Multiplication upper–bound

+
+
+  let ghost mult_bound (a b c : int)
+    requires { 0 < a * b <= c }
+    ensures { abs a <= c }
+    ensures { abs b <= c }
+    = ()
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.List.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.List.html new file mode 100644 index 00000000..13c07ac4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.List.html @@ -0,0 +1,34 @@ + + + + + +Module std.List + + +
indexlibrary stdmodule List
+
+module List
+  use export list.List
+  use export list.Map
+  use export list.Append
+
+
+

Appends an element to the end of the list

+
+
+  function push (xs : list 'a) (x : 'a) : list 'a =
+    xs ++ Cons x Nil
+
+  let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a)
+    ensures { map f (xs ++ ys) = map f xs ++ map f ys }
+    {proof}
+    variant { xs }
+    = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.index.html new file mode 100644 index 00000000..fe20174f --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.index.html @@ -0,0 +1,16 @@ + + + + + +Library std + + +
indexlibrary std
+
module Int
+
module List
+
module Clib
+
module Cfloat
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/std.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.proof.html new file mode 100644 index 00000000..ceb91d5d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/std.proof.html @@ -0,0 +1,63 @@ + + + + + +Library std + + +
indexlibrary stdproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  492ms
+  z3           @4.13.4 n=33  865ms
+
+

Proofs

+
module std.Int
+
+  goal euclide
+    alt-ergo 47ms
+  goal mult_bound
+    z3 6ms
module std.List
+
+  goal map_concat
+    alt-ergo 18ms
module std.Clib
+
+  value to_uint32
+  value is_null
+
+
+  goal valid_in_range
+    alt-ergo 14ms
+  goal ([])
+    alt-ergo 11ms
+  goal ([])
+    alt-ergo 11ms
+  goal ([]<-)
+    alt-ergo 15ms
+  goal slice
+    alt-ergo 14ms
+  goal slice_append
+    alt-ergo 29ms
+  goal vector_push
+    z3 16ms
module std.Cfloat
+
+  value f32
+  value f64
+  value (.+)
+  value (.-)
+  value (.-)
+  value (.*)
+  value (./)
+  value (.=)
+  value (.<)
+  value (.<=)
+
+
+  goal zero
+    split_vc
+  goal one
+    split_vc
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/style.css b/safety-related-profile/sonnx/ops/code/where/generated_doc/style.css new file mode 100644 index 00000000..1c6997e4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/style.css @@ -0,0 +1,138 @@ +/* -------------------------------------------------------------------------- */ +/* --- CSS Style Sheet for why3find doc generator --- */ +/* -------------------------------------------------------------------------- */ + +/* -------------------------------------------------------------------------- */ +/* --- Links --- */ +/* -------------------------------------------------------------------------- */ + +a { + color: teal; + outline: none; + text-decoration: none; +} + +a:visited { + color: teal; +} + +a:hover { + background: lightgreen; +} + +a:target { + background: lightblue; +} + +/* -------------------------------------------------------------------------- */ +/* --- Header Styling --- */ +/* -------------------------------------------------------------------------- */ + +header { + padding: 4px ; + color: darkgrey ; + border-bottom: thin solid darkgray ; +} + +header a { color: grey !important } + +/* -------------------------------------------------------------------------- */ +/* --- Table Of Contents --- */ +/* -------------------------------------------------------------------------- */ + +nav { display: none; } + +/* -------------------------------------------------------------------------- */ +/* --- Documentation Styling --- */ +/* -------------------------------------------------------------------------- */ + +div.doc { + /* Documentation block styling */ +} + +/* -------------------------------------------------------------------------- */ +/* --- Source Code Styling --- */ +/* -------------------------------------------------------------------------- */ + +pre.src { + /* Source code block styling */ +} + +.scope { + color: teal; +} + +.keyword { + color: purple; +} + +.admitted { + color: #DA2525; +} + +.attribute { + color: green; +} + +.comment { + color: grey; +} + +/* -------------------------------------------------------------------------- */ +/* --- Proof Marks --- */ +/* -------------------------------------------------------------------------- */ + +.icon { + margin-left: 2px ; + vertical-align: text-top ; + font-size: small ; + text-decoration: none ; +} + +.icon.small { font-size: x-small } + +.valid { color: green !important } +.failed { color: red !important } +.warning { color: orange !important } +.remark { color: lightgrey !important } +.prover:hover { color: purple } + +/* -------------------------------------------------------------------------- */ +/* --- Foldable Section --- */ +/* -------------------------------------------------------------------------- */ + +.section { } + +.section.level1:hover { + background: #fcfce1; +} + +.section.level2:hover { + background: #e9ffe9; +} + +.section.level3:hover { + background: #fff6f6; +} + +.section-toggle:hover { + background: lightgreen; +} + +.section-toggle { + cursor: zoom-in ; +} + +.section-toggle.active { + cursor: zoom-out ; +} + +.section-text { + display: none; +} + +.section-text.active { + display: inline; +} + +/* -------------------------------------------------------------------------- */ diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.OPWhere.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.OPWhere.html new file mode 100644 index 00000000..c3a52269 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.OPWhere.html @@ -0,0 +1,34 @@ + + + + + +Module tensor.OPWhere + + +
indexlibrary tensormodule OPWhere
+
+
+

OP–Where Tensor Operation

+
+
+module OPWhere
+  use Tensor
+
+  let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+    = fun ks -> if c ks then a ks else b ks
+
+  let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+    requires { a ~= b }
+    requires { c ~ a ~ b }
+    ensures { result ~= a ~= b }
+    ensures { result = dwhere c a b }
+    {proof}
+    = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Range.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Range.html new file mode 100644 index 00000000..fb4549e2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Range.html @@ -0,0 +1,80 @@ + + + + + +Module tensor.Range + + +
indexlibrary tensormodule Range
+
+module Range
+  use int.Int
+  use std.List
+
+  function size (ds : list int) : int =
+    match ds with
+    | Nil -> 1
+    | Cons d ds -> d * size ds
+    end
+
+  predicate positive (ds : list int) =
+    match ds with
+    | Nil -> true
+    | Cons d ds -> 0 < d /\ positive ds
+    end
+
+  predicate valid (ks ds : list int) =
+    match ks , ds with
+    | Nil , Nil -> true
+    | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds
+    | _ -> false
+    end
+
+  let rec lemma valid_push (ks ds : list int) (k d : int)
+    requires { 0 <= k < d }
+    requires { valid ks ds }
+    ensures { valid (push ks k) (push ds d) }
+    {proof}
+    variant { ks }
+    = match ks , ds with
+      | Nil , Nil -> ()
+      | Cons _ ks , Cons _ ds -> valid_push ks ds k d
+      | _ -> absurd
+      end
+    {qed}
+
+  let rec lemma size_append (xs ys : list int)
+    ensures { size (xs ++ ys) = size xs * size ys }
+    {proof}
+    variant { xs }
+    = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end
+    {qed}
+
+  lemma size_push: forall xs x. size (push xs x) = size xs * x
+
+  let rec lemma positive_size (ds : list int)
+    requires { positive ds }
+    ensures { 0 < size ds }
+    {proof}
+    variant { ds }
+    = match ds with Nil -> () | Cons _ ds -> positive_size ds end
+    {qed}
+
+  let rec lemma positive_valid (ks ds : list int)
+    requires { valid ks ds }
+    ensures { positive ds }
+    ensures { 0 < size ds }
+    {proof}
+    variant { ds }
+    = match ks , ds with
+      | Cons _ ks , Cons _ ds -> positive_valid ks ds
+      | _ -> ()
+      end
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Tensor.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Tensor.html new file mode 100644 index 00000000..bc50c46d --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.Tensor.html @@ -0,0 +1,112 @@ + + + + + +Module tensor.Tensor + + +
indexlibrary tensormodule Tensor
+
+
+

Formalization of Tensor

+
+
+module Tensor
+  use int.Int
+  use map.Map
+  use list.List
+  use Range
+
+  type data 'a = map (list int) 'a
+
+  type tensor 'a = {
+    dims : list int ;
+    data : data 'a ;
+    background : 'a ; (* default value, or value for 0-dimensions tensor *)
+  }
+  invariant { positive dims }
+  invariant { forall k. valid k dims \/ data k = background }
+  {proof}
+  by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d }
+  {qed}
+
+  meta coercion function dims
+  meta coercion function data
+
+
+

Tensor with the same dimensions

+
+
+  predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims
+
+
+

Tensor with the same dimensions and background value

+
+
+  predicate (~=) (a : tensor 'a) (b : tensor 'a) =
+    a ~ b /\ a.background = b.background
+
+
+

Equal tensors

+
+
+  predicate (==) (a b : tensor 'a) =
+    a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k
+
+
+

Extensionality

+
+
+  lemma exteq: forall a b : tensor 'a. a == b <-> a = b
+  {proof} by tensor'eq a b {qed}
+
+
+

Scalar Tensor

+
+
+  let ghost function scalar (v : 'a) : tensor 'a
+    ensures { result.dims = Nil }
+    ensures { result.background = v }
+    ensures { forall k. result k = v }
+    {proof}
+    = { dims = Nil ; data = (fun _ -> v) ; background = v }
+    {qed}
+
+
+

Null Tensor

+
+
+  let ghost function zero (e : 'a) (ds : list int) : tensor 'a
+    requires { positive ds }
+    ensures { result.dims = ds }
+    ensures { result.background = e }
+    ensures { forall k. result k = e }
+    {proof}
+    = { dims = ds ; data = (fun _ -> e) ; background = e }
+    {qed}
+
+
+

Constant Tensor

+
+
+  let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a
+    ensures { result.dims = ds }
+    requires { positive ds }
+    ensures { result.background = bg }
+    ensures { forall k. valid k ds -> result k = v }
+    {proof}
+    = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg }
+    {qed}
+
+
+

Constant & Null

+
+
+  goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.index.html new file mode 100644 index 00000000..8b3fe9a7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.index.html @@ -0,0 +1,20 @@ + + + + + +Library tensor + + +
indexlibrary tensor
+
+

Formalization of coordinates and dimensions

+
+
module Range
+
+
module Tensor
+
+
module OPWhere
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.proof.html new file mode 100644 index 00000000..9827c84c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/tensor.proof.html @@ -0,0 +1,51 @@ + + + + + +Library tensor + + +
indexlibrary tensorproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=16  171ms
+  cvc5         @1.2.1  n=42  489ms
+  z3           @4.13.4 n=30  140ms
+
+

Proofs

+
module tensor.Range
+
+  goal valid_push
+    alt-ergo 32ms
+  goal size_append
+    alt-ergo 28ms
+  goal size_push
+    alt-ergo 11ms
+  goal positive_size
+    alt-ergo 18ms
+  goal positive_valid
+    alt-ergo 29ms
module tensor.Tensor
+
+  goal tensor
+    alt-ergo 12ms
+  goal exteq
+    split_vc
+      alt-ergo 13ms
+      alt-ergo 13ms
+      alt-ergo 13ms
+  goal scalar
+    alt-ergo 15ms
+  goal zero
+    alt-ergo 12ms
+  goal const
+    alt-ergo 17ms
+  goal zero_is_const
+    alt-ergo 12ms
module tensor.OPWhere
+
+  goal dwhere
+    split_vc
+  goal opwhere
+    alt-ergo 15ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/where.CTensorWhere.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.CTensorWhere.html new file mode 100644 index 00000000..578cd279 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.CTensorWhere.html @@ -0,0 +1,58 @@ + + + + + +Module where.CTensorWhere + + +
indexlibrary wheremodule CTensorWhere
+
+module CTensorWhere
+  use tensor.std.Int
+  use tensor.std.List
+  use tensor.std.Clib
+  use tensor.std.Cfloat
+  use mach.int.Int32
+  use tensor.tensor.Range
+  use tensor.tensor.Tensor
+  use OPWhere
+  use tensor.layout.CFlat
+  use tensor.libvector.CIndex
+  use tensor.libtensor.CTensor
+
+
+
+let ctensor_where (cond a b r : ctensor) =
+    {proof}
+    requires { valid_tensor cond }
+    requires { valid_tensor a }
+    requires { valid_tensor b }
+    requires { valid_tensor r }
+    requires { tensor a ~= tensor b ~= tensor r }
+    {qed}
+    requires { tensorb cond ~ tensor a ~ tensor b }
+    ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) }
+    let m = cdim_size r.t_dims r.t_rank in
+    for i = 0 to m-1 do
+      {proof}
+      invariant {
+        forall k. 0 <= k < i ->
+          value_at r.t_data k =
+            if Real.(0.0 < value_at cond.t_data k)
+            then a.t_data[k] else b.t_data[k]
+      }
+      {qed}
+      r.t_data[i] <-
+        if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i]
+    done
+    {proof}
+    ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) }
+    {qed}
+
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/where.OPWhere.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.OPWhere.html new file mode 100644 index 00000000..b76d7899 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.OPWhere.html @@ -0,0 +1,34 @@ + + + + + +Module where.OPWhere + + +
indexlibrary wheremodule OPWhere
+
+
+

OP–Where Tensor Operation

+
+
+module OPWhere
+  use tensor.tensor.Tensor
+
+  let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a
+    = fun ks -> if c ks then a ks else b ks
+
+  let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a
+    requires { a ~= b }
+    requires { c ~ a ~ b }
+    ensures { result ~= a ~= b }
+    ensures { result = dwhere c a b }
+    {proof}
+    = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background }
+    {qed}
+
+end
+
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html new file mode 100644 index 00000000..90f575f5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.index.html @@ -0,0 +1,15 @@ + + + + + +Library where + + +
indexlibrary where
+
+
module OPWhere
+
module CTensorWhere
+ + + diff --git a/safety-related-profile/sonnx/ops/code/where/generated_doc/where.proof.html b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.proof.html new file mode 100644 index 00000000..32d0c44c --- /dev/null +++ b/safety-related-profile/sonnx/ops/code/where/generated_doc/where.proof.html @@ -0,0 +1,39 @@ + + + + + +Library where + + +
indexlibrary whereproofs
+

Provers

+
+  alt-ergo     @2.5.4  n=17  525ms
+  cvc5         @1.2.1  n=50  537ms
+  z3           @4.13.4 n=34  538ms
+
+

Proofs

+
module where.OPWhere
+
+  goal dwhere
+    alt-ergo 10ms
+  goal opwhere
+    alt-ergo 12ms
module where.CTensorWhere
+
+  goal ctensor_where
+    split_vc
+      alt-ergo 15ms
+      alt-ergo 19ms
+      alt-ergo 13ms
+      alt-ergo 41ms
+      alt-ergo 42ms
+      alt-ergo 44ms
+      alt-ergo 14ms
+      alt-ergo 28ms
+      alt-ergo 52ms
+      alt-ergo 167ms
+      alt-ergo 18ms
+      alt-ergo 22ms
+ + diff --git a/safety-related-profile/sonnx/ops/code/where/tests/README.md b/safety-related-profile/sonnx/ops/code/where/tests/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md b/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md new file mode 100644 index 00000000..2f4ad6e3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/c_code_generation/why3_to_c.md @@ -0,0 +1,152 @@ +# Converting Why3 Specifications to C Code + +This document outlines how to extract C code from Why3 (`.mlw`) specifications using the Why3 extraction mechanism and a C driver. It includes an example, supported features, and key constraints. + +## Compiling Why3 to C language +### Example + +This example defines a function `locate_max` that returns the index of the maximum element in an array `a` of size `n`. + +```whyml +use int.Int +use map.Map as Map +use mach.c.C +use mach.int.Int32 +use mach.int.Int64 + +function ([]) (a: ptr 'a) (i: int): 'a = Map.get a.data.Array.elts (a.offset + i) + +let locate_max (a: ptr int64) (n: int32): int32 + requires { 0 < n } + requires { valid a n } + ensures { 0 <= result < n } + ensures { forall i. 0 <= i < n -> a[i] <= a[result] } += let ref idx = 0 in + for j = 1 to n - 1 do + invariant { 0 <= idx < n } + invariant { forall i. 0 <= i < j -> a[i] <= a[idx] } + if get_ofs a idx < get_ofs a j then idx <- j + done; + idx + ``` + +### Extraction Command +```bash +why3 extract -D c locate_max.mlw +``` +With debug details: +```bash +why3 extract -D c --debug-all locate_max.mlw -o locate_max.c +``` +### Output C code +```c +#include + +int32_t locate_max(int64_t * a, int32_t n) { + int32_t idx; + int32_t j, o; + idx = 0; + o = n - 1; + if (1 <= o) { + for (j = 1; ; ++j) { + if (a[idx] < a[j]) { + idx = j; + } + if (j == o) break; + } + } + return idx; +} +``` + +## Supporte Features and Rules +### Basic Types + +| WhyML Type | Description | C Equivalent | +|----------------------|------------------------------------|------------------------| +| `int32`, `uint64`, etc. | From `mach.int` | `int32_t`, `uint64_t`, etc. | +| `bool` | Translated from `bool.Bool` | `int` | +| `char`, `string` | Partial support via `mach.c.String`| `char`, `char *` | +| `int` (mathematical) | ❌ Not supported | — | +| `float` | ❌ Not supported | — | +--- + +### Compound Types +#### Immutable Records + +**WhyML:** +```whyml +type r = { x : int32; y : int32 } +let swap (a : r) : r = { x = a.y ; y = a.x } +``` +**C:** +```c +struct r { + int32_t x; + int32_t y; +}; + +struct r swap(struct r a) { + struct r r; + r.x = a.y; + r.y = a.x; + return r; +} +``` + +#### Mutable Records +**WhyML:** +```whyml +type r = { mutable x : int32; mutable y : int32 } +let swap (a : r) : unit = + let tmp = a.y in a.y <- a.x; a.x <- tmp +``` +**C:** +```c +struct r { + int32_t x; + int32_t y; +}; + +void swap(struct r * a) { + int32_t tmp; + tmp = a->y; + a->y = a->x; + a->x = tmp; +} +``` +## Unsupported Features +- WhyML arrays + +- Algebraic data types (including enumerations) + +- Pattern matching + +- Exception raising and catching + +- Floating-point types + +## Control Stuctures + +| Feature | Support | +|----------------------------|---------| +| `if` / `else` | ✅ | +| `while` loops | ✅ | +| `for` loops | ✅ | +| Sequence (`;`) | ✅ | +| `break`, `continue`, `return` | ✅ | +| Pattern matching | ❌ | +| Exceptions | ❌ | + +## Notes +- **Example of driver file used for C extraction**: [`c.drv`](https://gitlab.inria.fr/why3/why3/-/blob/master/drivers/c.drv) +- This driver defines how WhyML types and functions are translated into equivalent C constructs. + +## References + +- [Why3 Documentation](https://why3.lri.fr/doc/) +- [Why3 GitLab Repository](https://gitlab.inria.fr/why3/why3) + + + + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/README.md b/safety-related-profile/sonnx/ops/docs/guidelines/README.md new file mode 100644 index 00000000..8738c7de --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/README.md @@ -0,0 +1,5 @@ +This folder contains the guidelines used to develop the SONNX artifacts: +- [Guidelines to carry out accuracy analysis](accuracy.md) +- [Guidelines to develop tests](./tests.md) +- [Guidelines to develop the non-formal specifications](./formal.md) +- [Guidelines to develop the formal specifications](./informal.md) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md b/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md new file mode 100644 index 00000000..2521a07f --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/accuracy.md @@ -0,0 +1,455 @@ +#### Objectives and limits + +This document provides theory and techniques to write a formal specification for the +accuracy of numerical components used in neural networks. The result is +a specification that should be verified by any implementation for the +considered hypotheses. Here is a list of acceptable hypotheses : + +* floating-point computation conformed to the IEEE-754 standard +* fixed-point computation +* concrete or symbolic range for the input values +* symbolic constraints over some computations + +There exists many possible specifications for the accuracy. For instance, the specification +for the matrix multiplication will be different if one matrix is diagonal or if both matrices +are dense with a same order of magnitude for every coefficients. + +We will favour short formulas, even if they may seem approximate. + +#### Numerical Accuracy + +This section provides a tight and verifiable specification of the numerical error +on the operator's results. The numerical error is the difference between the +current implementation on data with error resulting from previous approximated +computations and an ideal algorithm operating on data also coming from previous ideal +computations. + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +This framework decomposes the error into two parts: + +* the first, the propagated error, depends on the numerical + error and the numerical values of the inputs - in particular, it is independent of the + implementation and the storage format +* the second part, the introduced error, depends on the + concrete value of the inputs and the implementation with its storage format. + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$ + +The error associated with the result of the operator corresponds to the sum of the propagated +errors and the introduced error and this new error is then propagated by the next operator. + +The provided specification results from an over-approximated semantics (ex: IEEE-754) of the +numerical error of native computer operations approximating real number +operations. In order to preserve the readability of the formulas, the general specification introduces additional (conservative) simplifications compared to the original specifications. +However, this general specification may be too over-approximated for some specific inputs (ex tensor representing diagonal matrices). In this case, more precise specific specifications are provided alongside the general specification. + +The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries. + +###### Error Propagation + +This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator. + +**It is only for information**, since the properties does not depend on the implementation. +The formula aims to explain how an input error is amplified by the operator just from its +functional description. + +From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and +an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$. + +The **propagated error** of the $f$ function as ideal operator is + +$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$ + +Hence if $f$ is derivable two times, the formula + +$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) + \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$ + +is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$: + +$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$ + +If there are no error on the arguments, the propagated error is $0$. +Otherwise, we preconise a first order expression like: + +The absolute value of the propagated error is less or equal than + +$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$ + +**Example:** multiplication $f(x, y) = x\times y$ and division $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$ + +For the multiplication, the **propagated error** $PE(f)$ is + +$$\begin{array}{rcl} +PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\ + & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}} +\end{array}$$ + +that is simplified into + +$$\begin{array}{rcl} +PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$. + +Hence, the accuracy definition of the operator will just indicate that + +$$\begin{array}{rcl} +|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional, since it is always + +$$\begin{array}{rcl} + \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\ + & = & (x_{\textit{val}} + x_{\textit{err}})\times(y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} - (y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}})\\ + & = & x_{\textit{err}}\times y_{\textit{err}} +\end{array}$$ + +For the division, the **propagated error** $PE(g)$ is + +$$\begin{array}{rcl} +PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +that is simplified into + +$$\begin{array}{rcl} +PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional + +$$\begin{array}{rcl} + \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\ + & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +Hence, the accuracy definition of the operator will just indicate that + +$$\begin{array}{rcl} +|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +**Example:** 2D Matrix multiplication + +Tensor operators require a methodology to propose an **over-approximation of the propagated error**, +since the algorithm combine many atomic operations on $\mathbb{R}$. + +1. Naïve Algorithm Description + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // OAPE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + // |PE(C[i][j])| <= a*be + b*ae + C[i][j] += A[i][1]*B[1][j]; + // |PE(C[i][j])| <= 2*a*be + 2*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // |PE(C[i][j]| = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j]| <= k*a*be + k*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verify a loop induction, it becomes a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j])| <= k*a*be + k*b*ae + + int k = 2; + // if |PE(C[i][j])| <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |OAPE(C[i][j]| <= k*a*be + k*b*ae + // |PE(C[i][j])| <= p*a*be + p*b*ae + ``` + +3. Final specification + + At the end, we can specify that + + * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$, + * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$, + * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$, + * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$ + +###### Error Introduction + +This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$, +the introduced error, where $Y$ is the tensor result of an operator. +The objective is to provide a specification that any implementation should respect. Hence + +$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation. +It is always defined as the difference between the implementation result and the ideal result, for which we additionaly +substract the propagated error of the previous section. + +The introduced error is an over-approximation of difference between the implementation result and the ideal result +when there is no error on the input. + +For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator +whose result $r$ is a normal floating-point number, the introduced error is less or equal than +$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode +round to nearest even. Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval +may contain denormal numbers), the introduced error is less or equal than +$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even. +There exists more accurate formulas, but such formulas usually take too much +details into account. + +**Example:** multiplication $f(x, y) = x * y$ and division $g(x, y) = x / y$ + +For the multiplication, the **introduced error** $IE(f)$ should be less or equal than + +$$\begin{array}{rcl} +|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$|x|\times |y|$ is a normal number + +$$\begin{array}{rcl} +|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + +$$\begin{array}{rcl} +|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + + +$$\begin{array}{rcl} +|IE(f)| & < & 1 +\end{array}$$ + +for any fixpoint implementation. + +For the division, the **introduced error** $IE(g)$ should be less or equal than + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$\frac{|x|}{|y|}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & 1 +\end{array}$$ + +for any fixpoint implementation. + +**Example:** 2D Matrix multiplication + +Tensor operators require a methodology to find an **over-approximation of the introduced error**, +since their algorithms combine many atomic operations. + +1. Naïve Algorithm Description + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + // |IE(C[i][j])| <= a*b*u + // |C[i][j]| <= a*b*(1+u) + C[i][j] += A[i][1]*B[1][j]; + // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u) + // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)² + C[i][j] += A[i][2]*B[2][j]; + // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²) + // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verify a loop induction, it becomes a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + + int k = 2; + // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // if OAPE(C[i][j]) <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)² + ``` + +Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than + +$$\begin{array}{rcl} + |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\ + & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\ +\end{array}$$ + +where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$. + +###### Unit Verification + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +* The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +* The `float` field is a symbolic abstract domain for the computed value. +* The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +# Formal specification guidelines + +*To be completed.* diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png b/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png new file mode 100644 index 00000000..33123121 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/assets/domains.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png b/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png new file mode 100644 index 00000000..1ad0429b Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/assets/mcdc.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw new file mode 100644 index 00000000..f9df4fd7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/clip.mlw @@ -0,0 +1,27 @@ +module OPClip + use tensor.Tensor + use int.Int + use list.List + use tensor.Range + use real.Real + use real.MinMax + + predicate is_scalar_tensor (t : tensor real) = + t.dims = Nil /\ t.data = (fun _ -> t.background) + + function dclip (x l m : tensor real): data real + = fun ks -> + if valid ks x.dims then + min m.background (max (x.data ks) l.background) + else x.background + + let ghost function opclip (x l m : tensor real): tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result ~= x } + ensures { result.data = dclip x l m } + (*proof*) + = { dims = x.dims; data = dclip x l m; background = x.background } + (*qed*) + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw new file mode 100644 index 00000000..519ea716 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/conv.mlw @@ -0,0 +1,761 @@ +module OPConv2d + + use tensor.Tensor + use real.Real + use int.Int + use std.Int + use list.List + use int.EuclideanDivision + use tensor.Range + use list.Length + + function convDims (x w :tensor real) (str_h str_w :int) + (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : list int + = + let dY_0 = get_dim x.dims 0 in + let dY_1 = get_dim w.dims 0 in + + let alpha = get_dim x.dims 2 + pad_top + pad_bottom in + let theta = dir_h * (get_dim w.dims 2 - 1) + 1 in + let dY_2 = div (alpha - theta) str_h + 1 in + + let beta = get_dim x.dims 3 + pad_left + pad_right in + let gamma = dir_w * (get_dim w.dims 3 - 1) + 1 in + let dY_3 = div (beta - gamma) str_w + 1 in + + Cons dY_0 (Cons dY_1 (Cons dY_2 (Cons dY_3 Nil))) + + + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 + + let rec ghost function w_cools_calculate (x w : tensor real) (c: int) (i:int) (j:int) (jj:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { j - jj } + = + if jj >= j then + 0.0 + else + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons jj Nil)))) in + Real.(x_val * w_val + w_cools_calculate x w c i j Int.(jj + 1) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + + let rec lemma w_cools_split (x w : tensor real) (c i j jj k n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { jj <= k < j } + variant { k - jj } + ensures { + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + k * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons k Nil)))) in + w_cools_calculate x w c i Int.(k+1) jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(x_val * w_val + w_cools_calculate x w c i k jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if jj < k then w_cools_split x w c i j Int.(jj+1) k n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_lines_calculate (x w : tensor real) (c: int) (i:int) (ii:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { i - ii } + = + if ii >= i then + 0.0 + else + w_cools_calculate x w c ii j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c i Int.(ii + 1) j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_lines_split (x w : tensor real) (c i ii k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { ii <= k < i } + variant { k - ii } + ensures { + w_lines_calculate x w c Int.(k+1) ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_cools_calculate x w c k j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c k ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if ii < k then w_lines_split x w c i Int.(ii+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_channels_calculate (x w :tensor real) (c:int )(cc:int) (i:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { c - cc } + = + if cc >= c then + 0.0 + else + w_lines_calculate x w cc i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w c Int.(cc + 1) i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_channels_split (x w : tensor real) (c i cc k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { cc <= k < c } + variant { k - cc } + ensures { + w_channels_calculate x w Int.(k+1) cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_lines_calculate x w k i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w k cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if cc < k then w_channels_split x w c i Int.(cc+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + function dconv (x w : tensor real) (y_dims : list int) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : data real + = + let c = get_dim w.dims 1 in + let i = get_dim w.dims 2 in + let j = get_dim w.dims 3 in + fun ks -> + if valid ks y_dims then + let n = get_dim ks 0 in + let m = get_dim ks 1 in + let y_h = get_dim ks 2 in + let y_w = get_dim ks 3 in + w_channels_calculate x w c 0 i j n m y_h y_w str_h str_w dir_h dir_w pad_top pad_left + else + x.background + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons d ds -> get_dim_positive ds end + + let ghost function conv (x w : tensor real) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : tensor real + (*Strides [C1]*) + requires { str_h > 0 /\ str_w > 0 } + (*Pads [C1], [C2] *) + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + (*Dilations [C1], [C2]*) + requires { dir_h > 0 /\ dir_w > 0 } + (*X [C1],[C4]*) + requires { length x.dims = 4 } + (*X [C2] *) + requires { get_dim x.dims 1 = get_dim w.dims 1 } + (* W [C4]*) + requires { length w.dims = 4 } + (* Strides [C2] *) + requires { let y_shape = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + = + let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + { dims = y_dims; + data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right; + background = x.background; + } +end + + +module COPConv2d + + use OPConv2d + use tensor.Tensor + use tensor.Range + use real.Real + use int.Int + use list.List + use layout.CFlat + + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + use std.Int + use list.Length + use layout.CFlat + use tensor.Range + + + let rec lemma ivector_get_dim_value_at (coords: iarray) (n: int) (i: int) + requires { n = 4 } + requires { 0 <= i < n } + requires { valid_range coords 0 n } + ensures { get_dim (ivector coords n) i = value_at coords i } + variant { i } + = + if i > 0 then + ivector_get_dim_value_at coords n (i - 1) + + + + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end + + + let conv2d (x w r : ctensor) (str_h str_w : int32) (dir_h dir_w : int32) + (pad_top pad_left pad_bottom pad_right : int32) : ctensor + + requires { valid_tensor x} + requires { valid_tensor w} + requires { valid_tensor r} + requires { x.t_rank = w.t_rank = r.t_rank = 4 } + requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right } + ensures { (tensor r).dims = convDims (tensor x) (tensor w) str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right } + = + r + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + let w_cools_calculate (x w: ctensor) (c: int32) (i:int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + requires { 0 <= i < value_at w.t_dims 2 } + + (*Try to remove this block like Loic do it*) + requires { in_bounds (i * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { in_bounds (y_h * str_h + i * dil_h) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + requires { let x_h = y_h * str_h + i * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + ensures { let (value, flag) = result in + flag -> w_cools_calculate (tensor x) (tensor w) c i (value_at w.t_dims 3) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let ref flag = False in + let ref sum = (f32 0.0) in + let x_coords_array = malloc (to_uint32 4) in + let w_coords_array = malloc (to_uint32 4) in + let cols = w.t_dims[3] in + if is_not_null x_coords_array && is_not_null w_coords_array then begin + flag <- True; + for jj = 0 to cols - 1 do + invariant { sum = w_cools_calculate (tensor x) (tensor w) c i jj 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + x_coords_array[0] <- n; + x_coords_array[1] <- c; + x_coords_array[2] <- x_h; + x_coords_array[3] <- x_w; + assert { ivector x_coords_array 4 = Cons (to_int n) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) } ; + let (x_val, aux_flag) = coords_from_X_p x x_coords_array pad_top pad_left in + assert { aux_flag -> x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + if aux_flag then begin + w_coords_array[0] <- m; + w_coords_array[1] <- c; + w_coords_array[2] <- i; + w_coords_array[3] <- jj; + + ghost + begin + valid_bounds_2 w_coords_array w.t_dims 0 4; + assert { Range.valid (ivector w_coords_array w.t_rank) (ivector w.t_dims w.t_rank) }; + w_cools_split (tensor x) (tensor w) (to_int c) (to_int i) (to_int cols) 0 (to_int jj) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + + + let w_coords = coffset w_coords_array w.t_dims w.t_rank in + let w_val = w.t_data[w_coords] in + assert {Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil))) = ivector w_coords_array w.t_rank } ; + assert { (tensor w).Tensor.data (Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil)))) = w_val } ; + assert { x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + sum <- sum .+ (x_val .* w_val); + assert { sum = w_cools_calculate (tensor x) (tensor w) c i Int.(jj+1) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + end + else begin + flag <- False; + (f32 0.0, flag) + end + + let w_lines_calculate (x w : ctensor) (c: int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + + (*Try to remove this block like Loic do it*) + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_lines_calculate (tensor x) (tensor w) c (value_at w.t_dims 2) 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let i = w.t_dims[2] in + let ref flag = False in + let ref sum = (f32 0.0) in + for ii = 0 to i - 1 do + invariant { sum = w_lines_calculate (tensor x) (tensor w) c ii 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let (value, aux_flag) = w_cools_calculate x w c ii n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + ghost + begin + w_lines_split (tensor x) (tensor w) (to_int c) (to_int i) 0 (to_int ii) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let w_channels_calculate (x w : ctensor) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + + (*Try to remove this block like Loic do it*) + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_channels_calculate (tensor x) (tensor w) (to_int(value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) = value + } + = + let c = w.t_dims[1] in + let ref flag = False in + let ref sum = (f32 0.0) in + for cc = 0 to c - 1 do + invariant { sum = w_channels_calculate (tensor x) (tensor w) cc 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) } + let (value, aux_flag) = w_lines_calculate x w cc n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + (*Lemma is not needed on this function*) + (* + ghost + begin + w_channels_split (tensor x) (tensor w) (to_int c) (to_int(value_at w.t_dims 2)) 0 (to_int cc) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + *) + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let c_conv (x w r : ctensor) (str_h str_w : int32) (dil_h dil_w : int32) + (pad_top pad_left pad_bottom pad_right : int32) : bool + + requires { valid_tensor x} + requires { valid_tensor w} + requires { valid_tensor r} + requires { x.t_rank = w.t_rank = r.t_rank = 4 } + requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right } + (*Strides [C1]*) + requires { str_h > 0 /\ str_w > 0 } + (*Pads [C1], [C2] *) + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + (*Dilations [C1], [C2]*) + requires { dil_h > 0 /\ dil_w > 0 } + (*X [C1],[C4]*) + requires { length (tensor x).dims = 4 } + (*X [C2] *) + requires { get_dim (tensor x).dims 1 = get_dim (tensor w).dims 1 } + (* W [C4]*) + requires { length (tensor w).dims = 4 } + (* Strides [C2] *) + requires { let y_shape = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + + (*Try to remove this block like Loic do it*) + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { forall y_h: int. 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h) } + requires { forall ii y_h:int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { forall y_w: int. 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w) } + requires { forall jj y_w:int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii y_h: int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj y_w: int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + requires { forall mm:int. 0 <= mm < value_at r.t_dims 1 -> mm < value_at w.t_dims 0 } + + ensures { result -> tensor r = conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } + + = + let n = r.t_dims[0] in + let m = r.t_dims[1] in + let y_h = r.t_dims[2] in + let y_w = r.t_dims[3] in + let r_coords_array = malloc (to_uint32 4) in + if is_not_null r_coords_array then begin + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + + assert { ivector r_coords_array r.t_rank = Cons ( nn) (Cons ( mm) (Cons ( y_hh) (Cons ( y_ww) Nil))) } ; + assert { value = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left } ; + assert { let r_coords = CFlat.offset (ivector r_coords_array r.t_rank) (ivector r.t_dims r.t_rank) in + value_at r.t_data r_coords = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left }; + + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; + + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; + + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + ) + }; + + assert { forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + )} + end + else + return false + done; + done; + done; + done; + assert { tensor r == conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } ; + true + end + else + false + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw new file mode 100644 index 00000000..162e46e5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/dot_product.mlw @@ -0,0 +1,101 @@ +module OPMatmul + + use tensor.Tensor + use std.Int + use list.List + use tensor.Range + use list.Length + use int.Int + use real.Real + + + let rec ghost function dot_product_rec (a b: tensor real) (row col i iter: int) : real + variant { Int.(iter - i) } + = + if Int.(>=) i iter then + 0.0 + else + let a_val = a.data (Cons row (Cons i Nil)) in + let b_val = b.data (Cons i (Cons col Nil)) in + Real.(a_val * b_val + dot_product_rec a b row col Int.(i + 1) iter) + +end + + +module COPMatmul + + use OPMatmul + use tensor.Tensor + use tensor.Range + use real.Real + use int.Int + use list.List + use layout.CFlat + + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + use std.Int + use list.Length + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) = + requires { valid_tensor a } + requires { valid_tensor b } + requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter } + requires { a.t_rank = b.t_rank = 2 } + requires { iter >= 0 } + requires { row >= 0 /\ row < value_at a.t_dims 0 } + requires { col >= 0 /\ col < value_at b.t_dims 1 } + ensures { let (value, flag) = result in + flag -> value = dot_product_rec (tensor a) (tensor b) row col 0 iter + } + let ref sum = (f32 0.0) in + let ref flag = False in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + if is_not_null a_coords_array && is_not_null b_coords_array then begin + flag <- True; + for i = 0 to iter - 1 do + invariant { sum = dot_product_rec (tensor a) (tensor b) row col 0 i } + set_ofs a_coords_array 0 row; + set_ofs a_coords_array 1 i; + set_ofs b_coords_array 0 i; + set_ofs b_coords_array 1 col; + + let a_coords = coffset a_coords_array a.t_dims a.t_rank in + let a_val = a.t_data[a_coords] in + let b_coords = coffset b_coords_array b.t_dims b.t_rank in + let b_val = b.t_data[b_coords] in + + assert {Cons ((to_int row)) (Cons (i) Nil) = ivector a_coords_array a.t_rank }; + assert { (tensor a).Tensor.data (Cons ((to_int row)) (Cons (i) Nil)) = value_at a.t_data a_coords }; + + assert {Cons (i) (Cons ((to_int col)) Nil) = ivector b_coords_array b.t_rank }; + assert { (tensor b).Tensor.data (Cons (i) (Cons ((to_int col)) Nil)) = value_at b.t_data b_coords }; + + sum <- sum .+ (a_val .* b_val); + done; + (sum, flag) + end + else + (sum, flag) + +end diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw new file mode 100644 index 00000000..4f8482a7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/flatten.mlw @@ -0,0 +1,147 @@ +module OPFlatten + + use tensor.Tensor + use tensor.Range + use layout.CFlat + use int.Int + use real.Real + use std.Int + use list.List + use list.Length + + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons d ds -> get_dim_positive ds end + + + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) + + + let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) + + let rec ghost function calculate_dY1 (x : tensor real) (axis : int) (i : int) : int + requires { 0 <= axis <= length x.dims } + requires { axis <= i <= length x.dims } + variant { length x.dims - i } + = + if Int.(i >= length x.dims) then + 1 + else + get_dim x.dims i * calculate_dY1 x axis Int.(i + 1) + + let rec lemma calculate_dY1_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { axis <= i <= length x.dims } + ensures { 0 < calculate_dY1 x axis i } + variant { length x.dims - i } + = + if i >= length x.dims then + () + else + let d = get_dim x.dims i in + assert { 0 <= i < length x.dims }; + calculate_dY1_positive x axis (i + 1) + + function calculate_dims (x : tensor real) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) + + + function dflatten (x : tensor real) (y_shape: list int) : data real + = + fun ks -> + if valid ks y_shape then + let y_coords_flat = offset ks y_shape in + let x_coords = index y_coords_flat x.dims in + x.data x_coords + else + x.background + + function normalize_axis (axis : int) (x_rank : int) : int + = + if axis < 0 then + axis + x_rank + else + axis + + let ghost function flatten (x : tensor real) (axis : int) : tensor real + requires { -length x.dims <= axis <= length x.dims } + + ensures { let axis_normalized = normalize_axis axis (length x.dims) in + result.dims = calculate_dims x axis_normalized } + + ensures { result.background = x.background } + + ensures { let axis_normalized = normalize_axis axis (length x.dims) in + result.data = dflatten x (calculate_dims x axis_normalized) } + = + let axis_normalized = normalize_axis axis (length x.dims) in + let y_shape = calculate_dims x axis_normalized in + { dims = y_shape ; data = dflatten x y_shape ; background = x.background } + +end + + +module COPFlatten + + use OPFlatten + use tensor.Tensor + use list.List + use list.Length + use int.Int + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + + + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + assert { tensor r == flatten (tensor x) (to_int axis) } + +end + + + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw new file mode 100644 index 00000000..894748d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/matmul.mlw @@ -0,0 +1,43 @@ +module OPMatmul + use tensor.Tensor + use std.Int + use list.List + use tensor.Range + use list.Length + use int.Int + use real.Real + + let rec function dot_product (a b: tensor real) (row col i iter: int) : real + variant { Int.(iter - i) } + = + if Int.(>=) i iter then + 0.0 + else + let a_val = a.data (Cons row (Cons i Nil)) in + let b_val = b.data (Cons i (Cons col Nil)) in + Real.(a_val * b_val + dot_product a b row col Int.(i + 1) iter) + + function dmatmul ( a b: tensor real) (a_rows b_cols : int) : data real + = + let iter = get_dim a.dims 1 in + let y_shape = Cons a_rows (Cons b_cols Nil) in + fun ks -> + if valid ks y_shape then + let row = get_dim ks 0 in + let col = get_dim ks 1 in + dot_product a b row col 0 iter + else + a.background + + let ghost function matmul ( a b: tensor real) : tensor real + requires { a.background = b.background } + requires { length a.dims = length b.dims = 2 } + requires { get_dim a.dims 1 = get_dim b.dims 0 } + ensures { result.background = a.background } + ensures { result.dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) } + ensures { length result.dims = 2 } + ensures { result.data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) } + = + { dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) ; data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) ; background = a.background } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw new file mode 100644 index 00000000..7a545dc0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation.mlw @@ -0,0 +1,32 @@ +module OPSummation + use int.Int + use real.Real + use real.FromInt + + let rec function summation (i : int) (iter : int) : real = + variant { Int.(iter - i) } + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 +end + + +module COPSummation + use OPSummation + use mach.int.Int32 + use int.Int + use std.Cfloat + use real.Real + + let summation (iter: int32) : float = + requires { Int.(iter >= 0) } + requires { Int.(iter < max_int32) } + + let ref sum = (f32 0.0) in + for k = 0 to iter - 1 do + invariant { sum = summation 0 k } + sum <- sum .+ (f32 1.0); + done; + sum +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw new file mode 100644 index 00000000..04fdd9c5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/examples/summation2.mlw @@ -0,0 +1,41 @@ +module OPSummation + use int.Int + use real.Real + use real.FromInt + + let rec function summation (i : int) (iter : int) : real = + variant { Int.(iter - i) } + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 + + let rec lemma summation_correctness (i: int) (iter: int) + requires { Int.(0 <= i /\ i <= iter) } + variant { Int.(iter - i) } + ensures { summation i iter = from_int (Int.(iter - i)) } + = if i < iter then + summation_correctness (i + 1) iter + else + () +end + + +module COPSummation + use OPSummation + use mach.int.Int32 + use int.Int + use std.Cfloat + use real.Real + + let summation (iter: int32) : float = + requires { Int.(iter >= 0) } + requires { Int.(iter < max_int32) } + + let ref sum = (f32 0.0) in + for k = 0 to iter - 1 do + invariant { sum = summation 0 k } + sum <- sum .+ (f32 1.0); + done; + sum +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md new file mode 100644 index 00000000..78d69e61 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-ejn.md @@ -0,0 +1,1695 @@ +# SONNX — Formalization Guidelines + +**A structured guide for writing formal specifications of SONNX operators in Why3** + +--- + +## Table of Contents + +- [SONNX — Formalization Guidelines](#sonnx--formalization-guidelines) + - [Table of Contents](#table-of-contents) +- [Part 1 — Formalization Styles](#part-1--formalization-styles) + - [1.1 Abstract Formalization](#11-abstract-formalization) + - [Tensor](#tensor) + - [Specification Style](#specification-style) + - [Importance of this level](#importance-of-this-level) + - [1.2 Concrete Formalization](#12-concrete-formalization) + - [Tensor](#tensor-1) + - [Specification Style](#specification-style-1) + - [Importance of this level](#importance-of-this-level-1) + - [1.3 Link between the two levels](#13-link-between-the-two-levels) + - [Why is it important to have two levels?](#why-is-it-important-to-have-two-levels) +- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization) + - [2.1. Module Structure](#21-module-structure) + - [2.2 Function Declarations](#22-function-declarations) + - [2.2.1 Termination and Variants](#221-termination-and-variants) + - [2.2.2 Verification Conditions and Requires Clauses](#222-verification-conditions-and-requires-clauses) + - [2.2.3 TypeInvariant Lemmas](#223-typeinvariant-lemmas) + - [2.2.4 Main Operator Function](#224-main-operator-function) + - [2.2.5 Guidelines](#225-guidelines) + - [Examples](#examples) + - [2.3. Contracts on the Main Function](#23-contracts-on-the-main-function) + - [Example](#example) + - [2.4. Data Function Pattern](#24-data-function-pattern) + - [Anonymous function declaration](#anonymous-function-declaration) + - [Recursive dimensions constructs](#recursive-dimensions-constructs) + - [Examples](#examples-1) + - [2.5 Operator tensor types](#25-operator-tensor-types) +- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization) + - [3.1. Module Structure](#31-module-structure) + - [3.2 Auxiliary helper functions](#32-auxiliary-helper-functions) + - [3.3 Main operator function](#33-main-operator-function) + - [3.3.1 Contracts](#331-contracts) + - [3.4 Invariants](#34-invariants) + - [Loop Invariants for Proving Data Refinement](#loop-invariants-for-proving-data-refinement) + - [The innermost loop](#the-innermost-loop) + - [The outer loops](#the-outer-loops) +- [Part 4 - Tips, Hints and Strategies](#part-4---tips-hints-and-strategies) + - [4.1. Scope Resolution](#41-scope-resolution) + - [4.2. IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints) + - [Lemma / Axiom Instantiation](#lemma--axiom-instantiation) + - [Instantiation via Lemma Functions](#instantiation-via-lemma-functions) + - [Function Unfolding](#function-unfolding) + - [4.3. How to Debug](#43-how-to-debug) + - [Reading the Logical Context](#reading-the-logical-context) + + + +
+ +--- + +
+ + +> (eric) General (detail) remark: use **bold** characters only when it is absolutely necessary. + +# Part 1 — Formalization Styles + +In SONNX, every operator is formalized at **two distinct levels**. + +Each level serves a different purpose and follows a different style. + +> (eric) I would also add that these two levels are a way to lower gradually the specification down to a level where it can be translated automatically into C code. Without this need, we could stick to the first level only. + +Understanding both is essential before writing any specification. + + +## 1.1 Abstract Formalization + +At this level the operator must be specified based only on its behavior and **mathematical semantics**. + +> I would rather say that the specification shall make "no" reference (or "carry") implementation constraints. Providing an algorithm as a specification could be considered as an implementation solution but it is not. The algorithm is only one possible way to implement the operator and the choice of the actual implementation is completely left to the implementer. + +It should the **as close as possible transalation** of the mathematical definition of the operator - **presented in the informal specification**, being therefore easy to read and understand. + +> (eric) The formal specification shall be traceable to the informal specification. Traceability must be enforced +> - first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification) +> - second by providing explicit link via `[tags]` to specific parts of the informal specification. + +> (eric) "being easy to validate, first and easy to read and understand, second. +> Indeed, the translation from the informal specification to the formal specification cannot (yet) be performed automatically and will have to be checked manually. + +No implementation details should be present at this level, which means that the **tensor' representation** should **mimic the mathematical definition of a tensor** (also similar to the ONNX tensor concept) - a multidimensional array - in which each entry is accessed by its coordinates. + +> (eric) I am not sure that "mimic" is the appropriate word => "which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties. +> Nota that this may be arguable and (possibly) contradictory with the previous requirement about readability. Using arrays to represent tensors is something pretty familiar... Hopefully, wwe use "general indexes" in the informal spec that makes representing a tensor as a function from index to values pretty natural... + +### Tensor +> Tensor**s**? + +- **Type**: At this level tensors are said to be **polymorphic**.
+ +> (eroc) Maybe explain what you mean by "polymorphic", i.e., they must be parameterized by the type of the values "contained" in the tensor. +> (eric) They are not "said to be polymorphic", they **are** specified as polymorphic structures. + + This means that the specifier should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types). + +- **Representation**: Tensors are represented as structures containing the following records: + + - `dims` - List of integers representing the shape + + - `data` - A map from coordinates to values + +> (eric) In the informal specification, we use "[indexes](../../../spec/informal/common/definitions.md)" rather than "coordinates". + + - `background` - The default value for out-of-bounds coordinates + +> (eric) "for indexes out of the bounds defined by the shape"? + +- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representatio: + +> (eric) "representation" (missing "n") + + 1. **Positive Dimensions** + + All dimensions in the `dims` list of a tensor must be positive integers.
+ +> (eric) You mean "strictly positive integers" (otherwise I don't understand the following remark). + +From our point of view this will be updated to support **non-negative integers**, therefore allowing for tensors with null dimensions. [***Work in Progress***] +> (eric) Maybe: "This constraint may be relaxed in a lated version of the specification to support tensors with null dimensions. + + **Definition:** + + - Predicate `positive` + + ```why3 + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + ``` + - `invariant { positive dims }` + +> (eric) Stupid question : why `0 < d` rather than `d > 0`? + + 2. **Valid values** + + Coordinates are either valid for the tensor shape or the value mapped by those coordinates is the `background` value. + +> (eric) In the definition of the background value above, maybe use the term "valid index" since it is used here. +> +> (eric) A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor: + + **Definition:** + + - Predicate `valid` + + ```why3 + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + ``` + - `invariant { forall k. valid k dims \/ data k = background }` + +
+ + These invariants are extremely important as they will sometimes define how the operator should be formalized. + + For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant. + + > The previous sentence is a bit strange. Isn't this invariant a part of the specification of the tensor? + +### Specification Style + +Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output). + +Looping constructs are forbidden at this level, and instead, **recursive functions** and **mathematical constructs** should be used to define the behavior of the operator. + +> (eric) Again, this is not very compatible with the "principle" of simplicity and traceability stated earlier. May we should say something about "provability" or "ease of proof"? + +### Importance of this level + +It establishes the logical and mathematical specification of the operator, which serves as the **source of truth** for correctness. + +> (eric) why do you separate "logical" from "mathematical"? + +It also provides the perfect link between the informal specification and the formalization. + +## 1.2 Concrete Formalization + +The concrete level describes the **imperative implementation** that will be extracted to C code. + +> (eric) Stupid remark: Here, you use the work "imperative", as if the specification at the higher level was purely "declarative". But I feel that more often than not, our high-level specification are more "imperative" (they describe how to calculate something) than "declarative" (e.g., they describe the post-condition to be verified). + +It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory. + +### Tensor + +- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc..., and the operator should be defined specifically for that type. + +>(eric) "etc." + +- **Representation**: Tensors are represented as pointers to structures that contain: + + - `t_rank`: Number of dimensions + + - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array + + - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...) + +
+ +Note that, unlike the previous level, here the tensors are defined based on a concrete target representation - tensors are essentially flat arrays in memory. + +Moreover, there is no `background` value in this representation - **tensors entries must be accessed only in valid coordinates** (i.e., coordinates that are within the bounds of the tensor shape). + +> (eric) "only valid indexes must be used to access values of tensors" + +### Specification Style + +Throughout this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation. + +>(eric) "In this level..." + +### Importance of this level + +It provides the necessary details to extract executable C code and reason about memory, bounds, and machine integers. + +> (eric) To be consistent with what follows: "integers" => "data types" + +## 1.3 Link between the two levels + +### Why is it important to have two levels? + +- We need somehow to express that the **concrete implementation** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**. + +>(eric) "concrete implementation" or "concrete specification"? + +- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification. + +- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes. + +- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification. + +The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement: + + + +

+ Refinement Mapping Image +

+ +The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**. + +Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections. + +--- +
+ + +# Part 2 — Guidelines for Abstract Formalization + +## 2.1. Module Structure + +Each abstract module should follow this general structure: + + +``` +module OP + (* 1. Imports *) + (* 2. Auxiliary predicates *) + (* 3. Auxiliary helper functions *) + (* 4. Dimension-computing function *) + (* 5. Data-computing function *) + (* 6. Main operator function *) +end +``` + +Neither all the above sections are mandatory nor its order is fixed, however, it is recommended to follow this structure as much as possible for consistency and readability across different operator specifications. + +> (eric) On the contrary, shouldn't we enforce this structure and make it possible for a section to be left empty? + +On the other hand, **4**, **5** and **6** are usually written in such order as each one of them depends on the previous one. + + +## 2.2 Function Declarations + +Why3 supports several ways to declare functions, each with different purposes and objectives. + +We can have any of the following function signatures: + +> (eric) I have added some elements of description below: + +- `function`, which introduces a pure logical function that can be used in contracts, lemmas , etc. and cannot contain algorithmic code. + +- `let` + +- `let function`, which introduces a function that can be used both in code and specifications. + +- `let ghost function`, which is similar to `let function`, but that can only be used for verification. + +- `let rec function`, which introduces a recursive function that can be used both in code and specifications. + +- `let rec ghost function`, which is similar to a `let ghost function` but for a recursive function. + +- more information on this in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html) + +> (eric) A precise definition of these constructs is given in [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html). + +Ideally, according to Loïc's proposal, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used. + +> Remove reference to Loîc in the guidelines... Instead, recall why this is considered as good practice. + +> (eric) Todo (not necessarily by you!): give a few words of explanation on the major differences between these types of functions and their main usage. + + +However, this happens to be not always possible. + +### 2.2.1 Termination and Variants + +In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$. + +>(eric) link with previous paragraph to be modified. + + +```why3 +module OPSummation + use int.Int + + function summation (i : int) (iter : int) : int = + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` + +> This way of computing the sum seem pretty strange to me... sum (i) = if i > 0 then i + sum (i -1) else 0... but I guess that this was part of some more complicated function. + +In this case, if you attempt to open the why3 ide the following error will be shown: + +![Cannot prove termination error](./imgs/termination.png) + +In fact, `functions` are too weak to successfully prove recursion termination and therefore we need to help them prove termination. + +>(eric) "... we need to help the prover..." + +>(eric) It is not really that "function is too weak". It is the consequence of what a function denotes: a `function` is a purely logical construct that has no semantics of "execution", so it does not denote an algorithm, i.e, something that iterates and maintain some state. Consequently, it cannot have a "variant" (since nothing "variates"). + +To do so, we have to introduce a **variant** (some expression that repeatedly decreases with each recursive call). + +However, `function` **does not support contracts** and therefore **variants** are **forbidden**, so we need to switch to `let rec function` and provide the variant there. + +> (eric) Maybe should we explain things the other rund: when the function denotes an algorithm, then we have to use a `let [rec] function` and specify variants. + +```why3 + let rec function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` + +### 2.2.2 Verification Conditions and Requires Clauses + +Note that, functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**. + +> (eric) "Note that, pure logical functions declared by the keyword..." + +Changing the signature from `function` to `let rec function` will, on the other hand, generate such verification conditions. + +>(eric) We could possibly write that a `function` is a definition and, in that sense, it cannot be verified. Stated differently, a verification condition is only generated when there is something to be verified, such as a precondition before calling some function or a post condition after having executed a function. Here again, the key point is that to generate a verification condition, we need an algorithm, i.e., something to be executed. And a `function` does not specify an algorithm. + +That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions. + +To compare such different behaviors, check the examples below: + +**Explicitly need for requires clauses** + +```why3 + (** Helper function to extract element from list at given position **) + + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end +``` + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) +``` + +This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list. + +Consequently, verification conditions will be raised as is depicted in the picture below: + +> (eric) "raised" => "generated". +![Flatten abstract Verification Conditions - not proved](./imgs/flatten_VC_not_proved.png) + +In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions. + +Therefore, the code above should be adapted to include such contracts: + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + (...) +``` + +![Flatten abstract Verification Conditions - proved](./imgs/flatten_VC_proved.png) + +**No need for requires clauses** + +```why3 + function calculate_dims (x : tensor real) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) +``` +
+ +The code written above will not generate any verification conditions, even if the functions called inside it explicitly need **requires clauses**. + +> (eric) Maybe the word "called" is a bit misleading. As far as I understand, When a function is referred to in a pure logical function, as it is a definition, it only refers to the contract of the "called" function (pre, post) which define the function, but not describe how it behaves. + +Therefore, no verification condtions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left pannel in the picture above). + +> (eric) "condtions" => "conditions", "pannel" => "panel". + +### 2.2.3 TypeInvariant Lemmas + +When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form. + +> (eric) I am not sure to understand what you mean by "it may appear only...". + +As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties. + +The `calculate_dY0` function defined above is a concrete example of this issue. + +The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive: + +```why3 + let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) +``` + +The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints). + +### 2.2.4 Main Operator Function + +Moreover, the main operator function cannot be declared as `function` because it needs to have contracts (`requires` / `ensures`) and `function` does not support contracts. + +>(eric) Again, I feel as if we were presenting the problem the other way round : what we are specifying is an algorithm to compute the operator because we want to generate code. So, as we are dealing with an algorithm and not a pure logical definition, what we specify is a "let [rec) function."... +> (eric) But I am not sure why we use a ghost function for an operator since we want to generate code out of it. + +Therefore, the main operator function should be declared as `let ghost function` with full contracts. + +```why3 +module OPSummation + use int.Int + + let rec function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 + + let ghost function unitSum (n: int) : int = + requires { n >= 0 } + ensures { result = n } + summation 0 n +end +``` + +### 2.2.5 Guidelines + +| Guideline | Details | +|:---|:---| +| Use `function` for total functions and whenever it is possible! | A `function` is a logical function. | +| Use `let rec function` only when recursion needs a variant. | If Why3 cannot prove termination of a `function`, switch to `let rec function` and provide a `variant` clause. | +| Use `let ghost function` for the **main operator**. | The main function should be a `let ghost function` with full contracts (`requires` / `ensures`). | + +>(eric) I think that we should discuss those "guidelines" because I feel as if we were presenting things in the "wrong direction"... + +### Examples + +Besides the summation example above, here are some examples of such formalization styles: + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + +## 2.3. Contracts on the Main Function + +Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec`. + +The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions: + +| Postcondition | Purpose | +|:---|:---| +| `ensures { result.dims = ... }` | Specifies the **output shape** | +| `ensures { result.data = ... }` | Specifies the **output data** (as a map) | +| `ensures { result.background = ... }` | Specifies the **background value** | + +### Example + +```why3 +let ghost function opclip (x l m : tensor real) : tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result.dims = x.dims } (* ← shape *) + ensures { result.data = dclip x l m } (* ← data *) + ensures { result.background = x.background } (* ← background *) += + { dims = x.dims; data = dclip x l m; background = x.background } +``` + +## 2.4. Data Function Pattern + +Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern: + +> (eric) I am HERE. + +### Anonymous function declaration + +Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics. + +We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function. + +Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**. + +That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal). + +```why3 +let ghost function d (x: tensor a') (output_shape: list int) : data a' += +fun ks -> + if valid ks output_shape then + ... + else + +``` + +The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics. + +The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics. + +This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor. + +### Recursive dimensions constructs + +An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions. + +This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof. + +As a standard we will stick with the `anonymous function` definition. + +### Examples + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + + +## 2.5 Operator tensor types + +Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level. + +For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors. + +However, there are operator whose abstract tensor type can be completely polymorphic. + +Take a look ate the example below: + +```why3 +module OPWhere + use tensor.Tensor + + function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) +end +``` +
+ +Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`. + +Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**. + +Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic. + +So whenever possible one should resort to **polymorphic** abstract datatypes. + +This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others. + + +# Part 3 — Guidelines for Concrete Formalization + +## 3.1. Module Structure + +The concrete module should follow this general structure: + +```why3 + module COP + (* 1. Imports *) + (* 2. Auxiliary helper functions *) + (* 3. Main operator function *) + end +``` + +### 3.2 Auxiliary helper functions + +All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well. + +Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ... + +Under this context, translation means: + +- Defining the function in an imperative style + +- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping) + +To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw): + +```why3 + (* Abstract level *) + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 +``` + +```why3 + (* Concrete level *) + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end +``` + +These two functions perform the same exact computation. + +To better understand how these two relate one can compare both specifications: + +**Coordinates** + +- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator. + + - These functions are equivalent and such equivalence proof is already available in the tensors library + +- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps: + + - **Firstly**, the appropriate space is allocated with the `malloc` function + + - **If the malloc is successful**, we then set each index of the array with the appropriate value. + + - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause. + +**Computation** + +- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0. + +- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0. + +- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels. + +**Refinement mapping** + +- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by: + + ``` + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + ``` + +- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels. + +- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created. + +### 3.3 Main operator function + +This is naturally the most important function and the one whith more details to be taken into account. + +#### 3.3.1 Contracts + +The **main operator function** must include contracts at both levels. + +**Requires** + +- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +- On the **concrete level** we should have preconditions for: + + 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor** + + 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**. + + 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change). + +**Ensures** + +- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**: + +- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form: + + - `ensures { tensor result = (tensor x) ... }` + +The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw). + +- **Concrete level** + + ```why3 + let flatten (x r : ctensor) (axis: int32) + (* Requires*) + (* 1 - Valid tensors *) + requires { valid_tensor x } + requires { valid_tensor r } + + (* 2 - Shape match *) + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + + (* 3 - Informal Spec *) + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + + (* Ensures *) + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + + assert { tensor r == flatten (tensor x) (to_int axis) } + ``` + +**Requires** + +Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors. + +Expressing the **shape match** on the other hand is not that trivial. +There are multiple ways of doing this match. +The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same. +To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`. + +- `tensor`: Converts a **ctensor** into an **abstract tensor** + +- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int` + +- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important. + + - Takes as inputs: the **pointer** to convert and the **desired size** of conversion + +For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level. + +Translating the preconditions from the abstract level to the concrete level can be done in two different ways: + + 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions + + 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure. + +In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provavle than the other, so it is up to the specifier to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms: + +- `requires { size (tensor x) = size (tensor r) }` + +To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved. + +This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them. + +Check the following example: + +```why3 + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let ghost _ = flatten (tensor x) (to_int axis) in + (...) +``` + +This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator. + +The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved + +![Debugging VCs 0](./imgs/flatten_requires.png) + +If one had forgotten to include this precondition: + +``` +requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } +``` + +then, the strategy proposed above would have generated the following verification condition which is not automatically proved: + +![Debugging VCs 1](./imgs/flatten_requires_1.png) + +By inspecting the task menu, the specifier would be able to understand which precondition is missing and add it to the specification. + + + +**Ensures** + +Our final goal is to prove the **ensures** clause stating the refinement between both levels. + +Usually the following pipeline is followed to do so: + +$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$ + +Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof. + +### 3.4 Invariants + +**Nested Loop Invariants** + +Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**. + +A loop invariant must satisfy two proof obligations: + +1. **Initialization** — the invariant holds for the first iteration. + +2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive. + +When loops are nested, these obligations form a dependency chain: + +- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant + +- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant. + +The following example illustrates this: + +```why3 +let ref sum = (f32 0.0) in +(* ... *) +for i = 0 to rows - 1 do + invariant { sum = 0.0 } + for j = 0 to cols - 1 do + invariant { sum = 0.0 } + for k = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) i j 0 k } + (* ... *) + done; + sum <- 0.0; + done; +done; +``` + +The innermost loop (`k`) accumulates a dot product in `sum`. + +For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore: + +$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$ + +In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$. + +### Loop Invariants for Proving Data Refinement + +**Example 1 - Unit Summation** + +To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range. + +The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one. + +The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove: + +![Summation Invariant Preservation](./imgs/summation_invariant0.png) + +The goal and the respective logical context are below: + +![Summation Invariant Preservation](./imgs/summation_invariant1.png) + +What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration. + +To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level. + +Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1: + +```why3 + let rec lemma summation_lemma (i iter : int) + requires { i < iter } + ensures { 1 + summation (i + 1) iter = summation i iter } + variant { iter - i } + = + if i >= iter then + () + else + summation_lemma (i + 1) iter +``` + +After adding this, the invariant preservation is easily proved. + +This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation. + +**Example 2 - Dot Product** + +The example presented above is a very simple one, but the same strategy can be applied to much more complex operators. + +Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator. + +The file is available [here](./examples/dot_product.mlw). + +Once again the proof is not achievable and the task menu displays the following: + +![Dot Product Invariant Preservation](./imgs/dot_product0.png) + +To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma. + +The strategy is essentially the same: + +```why3 + let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int) + requires { Int.(i <= k < iter) } + variant { Int.(k - i) } + ensures { + let a_val = a.data (Cons row (Cons k Nil)) in + let b_val = b.data (Cons k (Cons col Nil)) in + dot_product_rec a b row col i Int.(k + 1) = + Real.(dot_product_rec a b row col i k + a_val * b_val) + } += + if i < k then + Real.(dot_product_lemma a b row col (i + 1) k iter) + else + () +``` + +This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma. + +![Dot Product Invariant Preservation](./imgs/dot_product1.png) + +To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables. + +We will do so by introducing a ghost block: + +```why3 +ghost begin + dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter) +end +``` + +After this, the invariant preservation is easily proved. + +More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints). + +**Convolution** + +The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops. + +Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates. + +Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above. + +The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration. + +This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate. + +The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement. + +Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor. + +Below are the four loop invariants used in the concrete Conv implementation: + +```why3 + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; +``` + +The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically. + +The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected. + +They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten. + +This must be asserted explicitly each time. + +### The innermost loop + +The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop): + +```why3 + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } +``` + +The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct: + +```why3 + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; +``` + +With these assertions in place, the proof of the innermost invariant succeeds. + +### The outer loops + +The preservation of the outer invariants still fails. + +To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant: + +```why3 +constant yy_hh : int + +H3 : 0 <=' yy_hh + +H2 : yy_hh <' (y_hh +' 1) + +constant yy_ww : int + +H1 : 0 <=' yy_ww + +H : yy_ww <' int32'int y_w + +constant coords : list int = + Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int)))) + +constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank)) + +------------------------------- Goal -------------------------------- + +goal c_conv'vc : + (value_at (r.t_data) @ offset1 coords dims).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h) + (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left) +``` + +Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant: + +``` +LoopInvariant2 : + forall yy_hh1:int. + 0 <=' yy_hh1 /\ yy_hh1 <' y_hh -> + (forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w -> + (let coords'unused = + Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r1.t_data) + @ offset1 + (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))) + (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 + (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3)) + nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w) + (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left))) +``` + +At first glance, the hypothesis and the goal look nearly identical. + +However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`. + +The only relationship between `r` and `r1` available in the logical context is: + +```why3 +H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data) +``` + +Meanwhile, from the innermost loop invariant, the following hypothesis is also available: + +``` +LoopInvariant1 : + forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) -> + (let coords'unused = + Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r.t_data) + @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))) + (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h) + (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left)) +``` + +The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices. + +This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop. + +The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant: + +``` +for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + +``` + +By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant. + +However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop. + +The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten. + +The same pattern of assertions resolves this: + +```why3 + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; +``` + +Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator. + +The completed file is available [here](./examples/conv.mlw). + +# Part 4 - Tips, Hints and Strategies + +## 4.1. Scope Resolution + +When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors. + +Take a look at the following example: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { iter - i } + = + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 +end +``` + +If you attempt to open the Why3 ide with the above code, you will get the following error: + +![Type error due to scope resolution](./imgs/scope.png) + +This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`. + +In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name. + +In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules. + +Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope. + +To solve the issue we will have to specify the scope operations on the `Int` domain: + + +$$(iter - i) \rightarrow Int.(iter - i)$$ + +$$i < iter \rightarrow Int.(i < iter)$$ + +$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$ + +The corrections applied to the code above are the following: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { Int.(iter - i) } + = + if Int.(i < iter) then + 1.0 + summation Int.(i + 1) iter + else + 0.0 +end +``` + +## 4.2. IDE Transformations and Prover Hints + +Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals. + +Below are techniques we have found useful in practice. + +The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE. + +--- + +### Lemma / Axiom Instantiation + +When an axiom or lemma is stated universally, for example: + +```why3 +axiom test: + forall x: int. P(x) -> Q(x) +``` + +The automated prover may either fail or take significantly longer to apply it in a specific context. + +Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful. + +**Example — Mean Value Theorem:** + +Consider the following axiom: + +```why3 +(* Lagrange Mean Value Theorem *) +axiom mean_value_theorem_sigmoid: + forall x y: real. + exists c: real. + (x <= c <= y \/ y <= c <= x) /\ + sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y) +``` + +And the following lemma to be proved: + +```why3 +lemma lipschitz_sigmoid: + forall x y: real. + abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y) +``` + +To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem. + +Instantiating the axiom with the specific variables `x` and `y` greatly helps. + +**IDE instantiation syntax:** + +``` +instantiate +``` + +If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step. + +The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it. + +This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE. + +--- + +### Instantiation via Lemma Functions + +Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved. + +This is the preferred approach, as it keeps the proof self-contained and reproducible. + +**Example — `valid_bounds_2` lemma function:** + +The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions: + +```why3 +let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end +``` + +Now consider the `dot_product` function, which allocates coordinate arrays at runtime: + +```why3 +let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) = + requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter } + requires { a.t_rank = b.t_rank = 2 } + requires { iter >= 0 } + requires { row >= 0 /\ row < value_at a.t_dims 0 } + requires { col >= 0 /\ col < value_at b.t_dims 1 } + (* ... *) + let ref sum = (f32 0.0) in + let ref flag = False in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + if is_not_null a_coords_array && is_not_null b_coords_array then begin + flag <- True; + for i = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) row col 0 i } + set_ofs a_coords_array 0 row; + set_ofs a_coords_array 1 i; + set_ofs b_coords_array 0 i; + set_ofs b_coords_array 1 col; + + ghost valid_bounds_2 a_coords_array a.t_dims 0 2; + (* Rest of code ... *) +``` + +Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own. + +Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue. + +--- + +### Function Unfolding + +When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle. + +Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE. + +**`unfold`** expands a specific function by its definition at the point where it appears in the goal. + +To use `unfold` transformation, enter the following command in the **"Type command here"** field: +``` +unfold +``` + +Before applying `unfold`: + +![before unfold](./imgs/before_unfold.png) + +After applying `unfold`: + +![after unfold](./imgs/after_unfold.png) + +**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously. + +It requires no arguments: + +``` +compute_in_goal +``` + +Result after applying `compute_in_goal`: + +![compute_in_goal](./imgs/compute_in_goal.png) + + +--- + +## 4.3. How to Debug + +"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal. + +There are two common root causes: + +1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated. + +2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces. + +The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap. + +### Reading the Logical Context + +Consider the following goal: + +``` +------------------------------- Goal -------------------------------- + +goal w_cools_calculate'vc : + to_extended_real max_value = + w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b) + (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) + (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom) + (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w) +``` + +In the logical context, among other hypotheses, we have: + +``` +constant x_val : float + +constant aux_flag : bool + +Ensures4 : + aux_flag = True -> + coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) + (to_int2 pad_left) = to_extended_real x_val + +constant ww1 : int321 + +constant ww : int = int32'int ww1 + +H31 : 0 <=' ww + +H30 : ww <=' int32'int o2 + +LoopInvariant : + to_extended_real max_value1 = + w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c) + (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h) + (to_int2 str_w) + +H1 : + w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0 + (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h) + (int32'int dil_w) (int32'int pad_top) (int32'int pad_left) + (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h) + (int32'int str_w) = + max_extended_real + (coords_from_X_p (tensor x) + (Cons (int32'int b) + (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int))))) + (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) + ...) + +Ensures : + to_extended_real (max max_value1 x_val) = + max_extended_real (to_extended_real max_value1) (to_extended_real x_val) + +constant max_value : float + +H : max_value = max max_value1 x_val +``` + +The logical context is sufficient to prove the goal. Here is the reasoning: + +**Left-hand side of the goal.** + +From `H`, we know `max_value = max max_value1 x_val`, so: + +``` +to_extended_real max_value = to_extended_real (max max_value1 x_val) +``` + +Applying `Ensures`: + +``` += max_extended_real (to_extended_real max_value1) (to_extended_real x_val) +``` + +**Right-hand side of the goal.** + +Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`: + +``` +w_cools_calculate ... (ww +' 1) ... + = max_extended_real + (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...) +``` + +From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`. + +For the first argument, `Ensures4` tells us (given `aux_flag = True`) that: + +``` +coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left) + = to_extended_real x_val +``` + +So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity). + +However, Why3 cannot discharge the goal automatically. + +**Why Why3 doesn't make the proof** + +The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor: + +``` +coords_from_X_p (tensor x) + (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) + (int32'int pad_top) (int32'int pad_left) +``` + +whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`. + +These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own. + +The connection must be made explicit with an intermediate assertion: + +```why3 +assert { ivector x_p_coords_array 4 = + Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) }; +``` + +This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md new file mode 100644 index 00000000..c34faab4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-em.md @@ -0,0 +1,1583 @@ +# SONNX — Formalization Guidelines + +==all coding examples in here should have syntax highlighting if possible== + +A structured guide for writing formal specifications of SONNX operators in Why3. + +This guidelines do not cover the basics of Why3, so familiarity with its syntax, semantics and features is expected. + +--- + +## Table of Contents + +- [Part 1 — Formalization Styles](#part-1--formalization-styles) + - [1.1 — Abstract Formalization](#11-abstract-formalization) + - [1.2 — Concrete Formalization](#12-concrete-formalization) + - [1.3 — Link between the two levels](#13-link-between-the-two-levels) +- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization) + - [2.1 — Module Structure](#21-module-structure) + - [2.2 — Function Declarations](#22-function-declarations) + - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function) + - [2.4 — Data Function Pattern](#24-data-function-pattern) + - [2.5 — Operator tensor types](#25-operator-tensor-types) +- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization) + - [3.1 — Module Structure](#31-module-structure) + - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions) + - [3.3 — Main operator function](#33-main-operator-function) + - [3.4 — Invariants](#34-invariants) +- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies) + - [4.1 — Scope Resolution](#41-scope-resolution) + - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints) + - [4.3 — How to Debug](#43-how-to-debug) + + + +
+ +--- + +
+ +# Part 1 — Formalization Styles + +In SONNX, every operator must be formalized at two distinct levels: abstract formalization and concrete formalization. + +Each level serves a different purpose and follows a different style. + +We start from an abstract formalization — which captures the mathematical semantics of the operator and serves as the **source of truth** for correctness — and progressively refine it into a concrete formalization that is close enough to the target implementation to be automatically extracted into C code - reasoning about memory, bounds, and machine data types. + +Understanding both is essential before writing any specification. + +## 1.1 Abstract Formalization + +At this level the operator must be specified making no reference to any implementation detail, and therefore it should be **implementation-agnostic**. + +It shall be **traceable** to the **informal specification**, being easy to validate, first and easy to read and understand, second. ==add link to informal spec== Traceability must be enforced: + +- first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification) + +- second by providing explicit link via `[tags]` to specific parts of the informal specification. + +No implementation details should be present at this level, which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties. + +### Tensors + +- **Type**: At this level tensors are specified as **polymorphic structures**, i.e., they are parameterized by the type of the values they contain.
+ This means that the specification author should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types). + +- **Representation**: Tensors are represented as structures containing the following records: + + - `dims` - List of integers representing the shape + + - `data` - A map from [indexes](../../../spec/informal/common/definitions.md) to values + + - `background` - The default value for indexes out of the bounds defined by the shape + + A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor + + The data mapping is complete since it maps all possible indexes to values, but the value mapped by invalid indexes is the `background` value. + +- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representation: + + 1. **Positive Dimensions** + + All dimensions in the `dims` list of a tensor must be **strictly positive integers**.
+ This constraint may be relaxed in a lated version of the specification to support tensors with **null** dimensions. [***Work in Progress***] + + - Predicate `positive` + + ```why3 + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + ``` + - `invariant { positive dims }` + + 2. **Valid values** + + Indexes are either valid for the tensor shape or the value mapped by those indexes is the `background` value. + + - Predicate `valid` ==the code below returns true for an empty index access to an empty tensor. This will be a problem when we introduce null tensors== + + ```why3 + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + ``` + - `invariant { forall k. valid k dims \/ data k = background }` + +
+ + These invariants are extremely important as they will sometimes define how the operator should be formalized. + + For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant. + + An alternative was to define the data through recursive definitions once the shape is already computed, but this approach is much more complex and usually requires auxiliary lemmas to help the proof. ==This sentence is a bit obscure for the casual reader. If the message is important, and it might be as it seems to contain a recommendation on how to do things, I suggest expanding the explanation to two-three sentences== + + The above predicates and invariants are available in the [tensors library](../../../spec/formal/common/libs/tensor/). + +### Specification Style + +Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output). + +Users should specify the operator based on mathematically and recursive definitions, avoiding loops and mutable state, increasing the **provability** and **ease of proof** of this specification. + +## 1.2 Concrete Formalization + +The concrete level describes the **imperative implementation** that will be extracted to C code. + +It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory. + +### Tensors + +- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc. and the operator should be defined specifically for that type. + +- **Representation**: Tensors are represented as pointers to structures that contain: + + - `t_rank`: Number of dimensions + + - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array ==What happens if the tensor dimension (or number of dimensions) is larger that the max value of int32? It is a ridicolous case in practice, but may happen in a cybersec attack scenario== + + - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...) + +
+ +Note that, unlike the previous level, here the tensors are defined based on a target C representation - tensors are essentially flat arrays in memory. + +Moreover, there is no `background` value in this representation - **only valid indexes must be used to access values of tensors** (i.e. coordinates that are within the bounds of the tensor shape).==Any rationale behind this choice, e.g. computational efficiency? What happens if we try to access an invalid index? Do we guarantee that it can never happen?== + +### Specification Style + +In this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation. + +### Importance of this level + +It provides the necessary details to extract executable C code and reason about memory, bounds, and machine data types. + +## 1.3 Link between the two levels + +### Why is it important to have two levels? + +- We need somehow to express that the **concrete specification** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**. + +- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification. + +- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes. + +- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification. ==Maybe "same result" is a little strong, especially regarding numerical error with floating point. I can easily see how the abstract specification defines a set of possible correct executions, and the concrete specification chooses only one (or a small subset of them)== + +The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement: + + + +

+ Refinement Mapping Image +

+ +The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**. ==Again, I can see that the result of the concrete execution is valid/compatible according to the abstract spec, but I feel equality will be impossible for floating point data types== + +Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections. + +--- +
+ +# Part 2 — Guidelines for Abstract Formalization + +## 2.1. Module Structure + +Each abstract module should follow this general structure: + + +``` +module OP + (* 1. Imports *) + (* 2. Auxiliary predicates *) + (* 3. Auxiliary helper functions *) + (* 4. Dimension-computing function *) + (* 5. Data-computing function *) + (* 6. Main operator function *) +end +``` + +If any section is not needed, it can be left empty, but the overall structure must be maintained for consistency and readability across different operator specifications. + +## 2.2 Function Declarations + +Why3 supports several ways to declare functions, each with different purposes and objectives. + +In order to fully understand each one of this features please take into account that whyml supports **two different programming namespaces**, a **logical** and a **programming** one, each one of them built upon a different syntax and with different features. For instance both of them support **conjunctions** but while the logical one expresses it through $\land$, the programming one expresses it through the `&&` operator. + + +We can have any of the following function signatures:==If an introduction to Why3 namespaces is really needed, I would prefer a more structured one. E.g. organise it by keyword (let, function, rec, ghost) rather than covering all possible combinations thereof.== + +- `function`: Belongs to the **logical namespace** and is used to define purely logical functions. Contracts are not supported. + +- `let`: Belongs to the **programming namespace** and defines a non recursive function. + +- `let rec`: Belongs to the **programming namespace** and defines a recursive function. + +- `let function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and allows them to be used at the logical namespace as well. + +- `let rec function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and allows them to be used at the logical namespace as well. + +- `let ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and **can only** be used at the **logical namespace**. + +- `let rec ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and **can only** be used at the **logical namespace**. + +- A precise definition of these constructs is given in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html) + +Ideally, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used. + +> Recall why this is considered as good practice.==This is probably the most important piece of information. Recording the "why" of your design choices is crucial== + +### 2.2.1 Termination and Variants + +==This subsection is a bit weak: (1) it contradicts the recommendation above that the abstract specification must only use the "function" keyword (by showing when to use "let rec ghost function"), (2) it is written in the style of a tutorial with examples, instead of a set of guidelines (e.g. "you shall only use function unless non-trivial recursion is absolutely needed")== + +It is not always possible to define all the necessary functions with the `function` construct, especially when we need to define recursive functions whose termination is not trivially provable by Why3. + +In order to understand that==,== have a look at the following module which computes the unit summation in the range $[0, n-1]$. This might not be the most traditional way of computing a summation, although, it intentionally resembles the iterative way of computing a summation, starting from 0 up to $n-1$. + +```why3 +module OPSummation + use int.Int + + function summation (i : int) (iter : int) : int = + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` +In this case, if you attempt to open the why3 ide the following error will be shown: + +![Cannot prove termination error](./imgs/termination.png) + +In fact, `functions` are not supposed to be used in recursive definitions, unless their termination is trivially provable by Why3 such as recursive calls over the tail of a list. ==So, function _can_ be recursive? How is "trivially provable" defined?== + +Intead one should use a `let rec ghost function` declaration, which allows to define recursive functions with a **variant** (some expression that repeatedly decreases with each recursive call) that can only be used in the logical namespace. + +```why3 + let rec ghost function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 +``` + +### 2.2.2 Verification Conditions and Requires Clauses + +Note that, pure logical functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**. ==Lack of guidelines here. Do we want all functions to specify pre-conditions? If not, do we have a rule to know which ones should?== + +Recalling the [definitions](#functions_def)==,== it is clear that `function` and `let rec ghost function` belong to different namespaces, although both of them can only be used at the logical namespace. As a consequence, since `let rec ghost function` is not a definition but an actual executable function, it generates verification conditions for all the functions called inside it that have preconditions. + +That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions. + +To compare such different behaviors, check the examples below: + +**Explicitly need for requires clauses** ==I find this example confusing for two reasons: (1) there is no explicit recommendation, so i do not know _why_ you are reminding me of this specific feature of Why3, (2) I cannot understand what calculate_dY0 does== + +```why3 + (** Helper function to extract element from list at given position **) + + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end +``` + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) +``` + +This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list. + +Consequently, verification conditions will be generated as is depicted in the picture below: + +![Flatten abstract Verification Conditions - not proved](./imgs/flatten_VC_not_proved.png) + +In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions. + +Therefore, the code above should be adapted to include such contracts: + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + (...) +``` + +![Flatten abstract Verification Conditions - proved](./imgs/flatten_VC_proved.png) + +**No need for requires clauses** + +```why3 + function calculate_dims (x : tensor real) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) +``` +
+ +The code written above will not generate any verification conditions, even if the functions referenced inside it explicitly need **requires clauses**. + +Therefore, no verification conditions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left panel in the picture above). + +### 2.2.3 TypeInvariant Lemmas + +When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form.=="sometimes" is a worryingly ambiguous thing to read in a guideline document. If it is well-known Why3 behaviour, add a link to further documentation== + +As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties.==Do we have a strict requirement that type invariants must be proven? Where is it listed? Add reference to relevant doc.== + +The `calculate_dY0` function defined above is a concrete example of this issue. + +The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive: + +```why3 + let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) +``` + +The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints). + +### 2.2.4 Main Operator Function + +To enforce traceability, as [previously mentioned](#traceability), the main operator function should capture the constraints present in the informal specification as preconditions (`requires`) while providing postconditions (`ensures`) for every record of the output tensor. + +To capture such contracts one needs to define a construct that belongs to the programming namespace. ==Does it mean we have to do this only for the concrete spec? The guidelines above explicitly state that we should use the logical namespace for the abstract spec...== Moreover, since this function is only used to specification purposes it is not supposed to be used in any implementation context. To enforce that this function can only be used at the logical level sign it as **ghost**.==To quote the beginning of Section 2.2 "at the abstract level we should only declare function with the signature function and no contracts should be used." But the rest of Section 2 seems to implicitly stress that contracts are _very important_, which is confusing== + +Therefore, the main operator function should be declared as `let rec ghost function` with full contracts. + +```why3 +module OPSummation + use int.Int + + let rec ghost function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 + + let ghost function unitSum (n: int) : int = + requires { n >= 0 } + ensures { result = n } + summation 0 n +end +``` + +### Examples + +Besides the summation example above, here are some examples of such formalization styles: + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + +## 2.3. Contracts on the Main Function + +Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec ghost function`. ==This is the first time you state this requirement. Indeed, in Sections 2.2.2 and 2.2.3 many examples of non-main functions containing the require keyword are given== + +The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions:==You literally just said "output constraints (such as shape) will NOT be included". This is extremely contradictory== + +| Postcondition | Purpose | +|:---|:---| +| `ensures { result.dims = ... }` | Specifies the **output shape** | +| `ensures { result.data = ... }` | Specifies the **output data** (as a map) | +| `ensures { result.background = ... }` | Specifies the **background value** | + +### Example + +```why3 +let ghost function opclip (x l m : tensor real) : tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result.dims = x.dims } (* ← shape *) + ensures { result.data = dclip x l m } (* ← data *) + ensures { result.background = x.background } (* ← background *) += + { dims = x.dims; data = dclip x l m; background = x.background } +``` + +## 2.4. Data Function Pattern + +Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern==s==: + + +### Anonymous function declaration + +Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics. + +We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function. + +Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**. + +That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal).==I see what you mean, but the section you link only gives the order in which functions should be declared in the code, which (as far as I understand the semantic of Why3) has no influence on the order of computation.== + +```why3 +let ghost function d (x: tensor a') (output_shape: list int) : data a' += +fun ks -> + if valid ks output_shape then + ... + else + +``` + +The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics. + +The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics. + +This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor. + +### Recursive dimensions constructs + +An alternative way would be to define as ==many== recursive helper functions as the number of dimensions and then define the data function based on these recursive functions. + +This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof. + +As a standard we will stick with the `anonymous function` definition. ==Why propose this alternative if it introduces complexity for no reason? Is there some operator for which the anonymous function approach is impossible?== + +### Examples + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + + +## 2.5 Operator tensor types + +Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level. + +For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors. + +However, there are operator whose abstract tensor type can be completely polymorphic. + +Take a look ==at== the example below: + +```why3 +module OPWhere + use tensor.Tensor + + function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) +end +``` +
+ +Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`. + +Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**. + +Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic. + +So whenever possible one should resort to **polymorphic** abstract datatypes. + +This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others. + + +# Part 3 — Guidelines for Concrete Formalization + +## 3.1. Module Structure + +The concrete module should follow this general structure: + +```why3 + module COP + (* 1. Imports *) + (* 2. Auxiliary helper functions *) + (* 3. Main operator function *) + end +``` + +### 3.2 Auxiliary helper functions + +All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well. + +Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ... + +Under this context, translation means: + +- Defining the function in an imperative style + +- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping) + +To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw): + +```why3 + (* Abstract level *) + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 +``` + +```why3 + (* Concrete level *) + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end +``` + +These two functions perform the same exact computation. + +To better understand how these two relate one can compare both specifications: + +**Coordinates** + +- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator. + + - These functions are equivalent and such equivalence proof is already available in the tensors library + +- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps: + + - **Firstly**, the appropriate space is allocated with the `malloc` function + + - **If the malloc is successful**, we then set each index of the array with the appropriate value. + + - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause. + +**Computation** + +- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0.==Aren't we supposed to return the background value, rather than 0?== + +- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0. + +- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels.==there is no postcondition on the coffset function in the code above== + +**Refinement mapping** + +- The refinement mapping relation is expressed with an ensures. In this function this ==relation== is expressed by: + + ``` + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + ``` + +- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels. + +- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created. + +### 3.3 Main operator function + +This is naturally the most important function and the one whith more details to be taken into account. + +#### 3.3.1 Contracts + +The **main operator function** must include contracts at both levels. + +**Requires** + +- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point.==the guidelines for the abstract specification are covered in Section 2, do not repeat them here== + +- On the **concrete level** we should have preconditions for: + + 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor** + + 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**. + + 3. All the **preconditions** already present at the abstract level. The **concrete** ==level== will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change). + +**Ensures** + +- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**:==already covered in Section 2: remove== + +- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form: + + - `ensures { tensor result = (tensor x) ... }` + +The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw). + +- **Concrete level** + + ```why3 + let flatten (x r : ctensor) (axis: int32) + (* Requires*) + (* 1 - Valid tensors *) + requires { valid_tensor x } + requires { valid_tensor r } + + (* 2 - Shape match *) + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + + (* 3 - Informal Spec *) + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + + (* Ensures *) + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + + assert { tensor r == flatten (tensor x) (to_int axis) } + ``` + +**Requires** + +Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors. + +Expressing the **shape match** on the other hand is not that trivial. +There are multiple ways of doing this match. +The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same. +To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`. + +- `tensor`: Converts a **ctensor** into an **abstract tensor** + +- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int` + +- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that=='==s why such convertion is important. + + - Takes as inputs: the **pointer** to convert and the **desired size** of conversion + +For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level. + +Translating the preconditions from the abstract level to the concrete level can be done in two different ways: + + 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions + + 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure. + +In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provable than the other, so it is up to the specification author to choose which one to use.==Which guideline should the spec author follow? Prioritise readability? Prioritise provability?== Having said so, the second **precondition** could also have been expressed through abstract terms: + +- `requires { size (tensor x) = size (tensor r) }` + +To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate ==whether== or not the verification conditions generated are automatically proved. ==you say "can", but do you mean "must" (since these are guidelines)?== + +This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them. + +Check the following example: ==The following is valuable advice, but not necessarily a "guideline". Perhaps put it in a different separate subsection and clearly mark it as "advice/tutorial". In general, there are a few of such practical advice sections throughout the document. All of them should be clearly marked== + +```why3 + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let ghost _ = flatten (tensor x) (to_int axis) in + (...) +``` + +This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator. + +The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved + +![Debugging VCs 0](./imgs/flatten_requires.png) + +If one had forgotten to include this precondition: + +``` +requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } +``` + +then, the strategy proposed above would have generated the following verification condition which is not automatically proved: + +![Debugging VCs 1](./imgs/flatten_requires_1.png) + +By inspecting the task menu, the specification author would be able to understand which precondition is missing and add it to the specification. + + + +**Ensures** + +Our final goal is to prove the **ensures** clause stating the refinement between both levels. + +Usually the following pipeline is followed to do so: + +$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$ + +Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof. + +### 3.4 Invariants + +**Nested Loop Invariants** + +Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**. + +A loop invariant must satisfy two proof obligations: + +1. **Initialization** — the invariant holds for the first iteration. + +2. **Preservation** — ==the invariant== continually holds for all iterations up to the last one, inclusive. + +When loops are nested, these obligations form a dependency chain: + +- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant + +- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant. + +The following example illustrates this: + +```why3 +let ref sum = (f32 0.0) in +(* ... *) +for i = 0 to rows - 1 do + invariant { sum = 0.0 } + for j = 0 to cols - 1 do + invariant { sum = 0.0 } + for k = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) i j 0 k } + (* ... *) + done; + sum <- 0.0; + done; +done; +``` + +The innermost loop (`k`) accumulates a dot product in `sum`. ==the code seems to overwrite sum, rather than accumulate it== + +For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. ==the notation $I-j$ appears from nowhere. Can you add it inside the code (e.g. as comments)?== In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore: + +$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$ + +In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$. + +### Loop Invariants for Proving Data Refinement + + ==The implicit SONNX guideline here is something like: "the invariants in the concrete specification must be automatically proven by Why3", then the rest is a tutorial on how to achieve it. I would prefer if this distinction is made clear.== + +**Example 1 - Unit Summation** + +To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range. + +The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one. + +The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove: + +![Summation Invariant Preservation](./imgs/summation_invariant0.png) + +The goal and the respective logical context are below: + +![Summation Invariant Preservation](./imgs/summation_invariant1.png) + +What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration. + +To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level. + +Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1: + +```why3 + let rec lemma summation_lemma (i iter : int) + requires { i < iter } + ensures { 1 + summation (i + 1) iter = summation i iter } + variant { iter - i } + = + if i >= iter then + () + else + summation_lemma (i + 1) iter +``` + +After adding this, the invariant preservation is easily proved. + +This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation. + +**Example 2 - Dot Product** + +The example presented above is a very simple one, but the same strategy can be applied to much more complex operators. + +Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator. + +The file is available [here](./examples/dot_product.mlw). + +Once again the proof is not achievable and the task menu displays the following: + +![Dot Product Invariant Preservation](./imgs/dot_product0.png) + +To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma. + +The strategy is essentially the same: + +```why3 + let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int) + requires { Int.(i <= k < iter) } + variant { Int.(k - i) } + ensures { + let a_val = a.data (Cons row (Cons k Nil)) in + let b_val = b.data (Cons k (Cons col Nil)) in + dot_product_rec a b row col i Int.(k + 1) = + Real.(dot_product_rec a b row col i k + a_val * b_val) + } += + if i < k then + Real.(dot_product_lemma a b row col (i + 1) k iter) + else + () +``` + +==I don't understand the expression $k + a_val * b_val$ in the code above, why are we mixing indices (k) and data?== + +This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma. + +![Dot Product Invariant Preservation](./imgs/dot_product1.png) + +To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables. + +We will do so by introducing a ghost block: + +```why3 +ghost begin + dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter) +end +``` + +After this, the invariant preservation is easily proved. + +More information regarding lemmas ==instantiation== in [4.2](#42-ide-transformations-and-prover-hints). + +==Example 3== - **Convolution** + +The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops. ==Is this a 2D convolution only? As far as I recall, the ONNX operator supports different sizes== + +Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates. + +Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above. + +The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration. + +This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate. + +The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement. + +Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor. + +Below are the four loop invariants used in the concrete Conv implementation: + +```why3 + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; +``` + +The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically. + +The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected. + +They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten. + +This must be asserted explicitly each time. + +### The innermost loop + +== The header is larger than the parent header (Example 3 - Convolution)== + +The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop): + +```why3 + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } +``` + +The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct: + +```why3 + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; +``` + +With these assertions in place, the proof of the innermost invariant succeeds. + +### The outer loops + +The preservation of the outer invariants still fails. + +To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant: + +```why3 +constant yy_hh : int + +H3 : 0 <=' yy_hh + +H2 : yy_hh <' (y_hh +' 1) + +constant yy_ww : int + +H1 : 0 <=' yy_ww + +H : yy_ww <' int32'int y_w + +constant coords : list int = + Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int)))) + +constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank)) + +------------------------------- Goal -------------------------------- + +goal c_conv'vc : + (value_at (r.t_data) @ offset1 coords dims).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h) + (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left) +``` + +Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant: + +``` +LoopInvariant2 : + forall yy_hh1:int. + 0 <=' yy_hh1 /\ yy_hh1 <' y_hh -> + (forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w -> + (let coords'unused = + Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r1.t_data) + @ offset1 + (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))) + (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 + (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3)) + nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w) + (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left))) +``` + +At first glance, the hypothesis and the goal look nearly identical. + +However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`. + +The only relationship between `r` and `r1` available in the logical context is: + +```why3 +H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data) +``` + +Meanwhile, from the innermost loop invariant, the following hypothesis is also available: + +``` +LoopInvariant1 : + forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) -> + (let coords'unused = + Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r.t_data) + @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))) + (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h) + (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left)) +``` + +The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices. + +This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop. + +The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant: + +``` +for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + +``` + +By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant. + +However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop. + +The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten. + +The same pattern of assertions resolves this: + +```why3 + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; +``` + +Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator. + +The completed file is available [here](./examples/conv.mlw). + +# Part 4 - Tips, Hints and Strategies + +==I did not review the below in detail== + +## 4.1. Scope Resolution + +When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors. + +Take a look at the following example: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { iter - i } + = + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 +end +``` + +If you attempt to open the Why3 ide with the above code, you will get the following error: + +![Type error due to scope resolution](./imgs/scope.png) + +This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`. + +In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name. + +In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules. + +Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope. + +To solve the issue we will have to specify the scope operations on the `Int` domain: + + +$$(iter - i) \rightarrow Int.(iter - i)$$ + +$$i < iter \rightarrow Int.(i < iter)$$ + +$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$ + +The corrections applied to the code above are the following: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { Int.(iter - i) } + = + if Int.(i < iter) then + 1.0 + summation Int.(i + 1) iter + else + 0.0 +end +``` + +## 4.2. IDE Transformations and Prover Hints + +Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals. + +Below are techniques we have found useful in practice. + +The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE. + +--- + +### Lemma / Axiom Instantiation + +When an axiom or lemma is stated universally, for example: + +```why3 +axiom test: + forall x: int. P(x) -> Q(x) +``` + +The automated prover may either fail or take significantly longer to apply it in a specific context. + +Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful. + +**Example — Mean Value Theorem:** + +Consider the following axiom: + +```why3 +(* Lagrange Mean Value Theorem *) +axiom mean_value_theorem_sigmoid: + forall x y: real. + exists c: real. + (x <= c <= y \/ y <= c <= x) /\ + sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y) +``` + +And the following lemma to be proved: + +```why3 +lemma lipschitz_sigmoid: + forall x y: real. + abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y) +``` + +To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem. + +Instantiating the axiom with the specific variables `x` and `y` greatly helps. + +**IDE instantiation syntax:** + +``` +instantiate +``` + +If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step. + +The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it. + +This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE. + +--- + +### Instantiation via Lemma Functions + +Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved. + +This is the preferred approach, as it keeps the proof self-contained and reproducible. + +**Example — `valid_bounds_2` lemma function:** + +The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions: + +```why3 +let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end +``` + +Now consider the `dot_product` function, which allocates coordinate arrays at runtime: + +```why3 +let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) = + requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter } + requires { a.t_rank = b.t_rank = 2 } + requires { iter >= 0 } + requires { row >= 0 /\ row < value_at a.t_dims 0 } + requires { col >= 0 /\ col < value_at b.t_dims 1 } + (* ... *) + let ref sum = (f32 0.0) in + let ref flag = False in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + if is_not_null a_coords_array && is_not_null b_coords_array then begin + flag <- True; + for i = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) row col 0 i } + set_ofs a_coords_array 0 row; + set_ofs a_coords_array 1 i; + set_ofs b_coords_array 0 i; + set_ofs b_coords_array 1 col; + + ghost valid_bounds_2 a_coords_array a.t_dims 0 2; + (* Rest of code ... *) +``` + +Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own. + +Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue. + +--- + +### Function Unfolding + +When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle. + +Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE. + +**`unfold`** expands a specific function by its definition at the point where it appears in the goal. + +To use `unfold` transformation, enter the following command in the **"Type command here"** field: +``` +unfold +``` + +Before applying `unfold`: + +![before unfold](./imgs/before_unfold.png) + +After applying `unfold`: + +![after unfold](./imgs/after_unfold.png) + +**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously. + +It requires no arguments: + +``` +compute_in_goal +``` + +Result after applying `compute_in_goal`: + +![compute_in_goal](./imgs/compute_in_goal.png) + + +--- + +## 4.3. How to Debug + +"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal. + +There are two common root causes: + +1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated. + +2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces. + +The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap. + +### Reading the Logical Context + +Consider the following goal: + +``` +------------------------------- Goal -------------------------------- + +goal w_cools_calculate'vc : + to_extended_real max_value = + w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b) + (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) + (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom) + (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w) +``` + +In the logical context, among other hypotheses, we have: + +``` +constant x_val : float + +constant aux_flag : bool + +Ensures4 : + aux_flag = True -> + coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) + (to_int2 pad_left) = to_extended_real x_val + +constant ww1 : int321 + +constant ww : int = int32'int ww1 + +H31 : 0 <=' ww + +H30 : ww <=' int32'int o2 + +LoopInvariant : + to_extended_real max_value1 = + w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c) + (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h) + (to_int2 str_w) + +H1 : + w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0 + (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h) + (int32'int dil_w) (int32'int pad_top) (int32'int pad_left) + (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h) + (int32'int str_w) = + max_extended_real + (coords_from_X_p (tensor x) + (Cons (int32'int b) + (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int))))) + (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) + ...) + +Ensures : + to_extended_real (max max_value1 x_val) = + max_extended_real (to_extended_real max_value1) (to_extended_real x_val) + +constant max_value : float + +H : max_value = max max_value1 x_val +``` + +The logical context is sufficient to prove the goal. Here is the reasoning: + +**Left-hand side of the goal.** + +From `H`, we know `max_value = max max_value1 x_val`, so: + +``` +to_extended_real max_value = to_extended_real (max max_value1 x_val) +``` + +Applying `Ensures`: + +``` += max_extended_real (to_extended_real max_value1) (to_extended_real x_val) +``` + +**Right-hand side of the goal.** + +Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`: + +``` +w_cools_calculate ... (ww +' 1) ... + = max_extended_real + (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...) +``` + +From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`. + +For the first argument, `Ensures4` tells us (given `aux_flag = True`) that: + +``` +coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left) + = to_extended_real x_val +``` + +So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity). + +However, Why3 cannot discharge the goal automatically. + +**Why Why3 doesn't make the proof** + +The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor: + +``` +coords_from_X_p (tensor x) + (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) + (int32'int pad_top) (int32'int pad_left) +``` + +whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`. + +These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own. + +The connection must be made explicit with an intermediate assertion: + +```why3 +assert { ivector x_p_coords_array 4 = + Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) }; +``` + +This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md new file mode 100644 index 00000000..b02c3ffa --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines-jlf.md @@ -0,0 +1,1597 @@ +# SONNX — Formalization Guidelines + +**A structured guide for writing formal specifications of SONNX operators in Why3** + +--- + +## Table of Contents + +- [Part 1 — Formalization Styles](#part-1--formalization-styles) + - [1.1 — Abstract Formalization](#11-abstract-formalization) + - [1.2 — Concrete Formalization](#12-concrete-formalization) + - [1.3 — Link between the two levels](#13-link-between-the-two-levels) +- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization) + - [2.1 — Module Structure](#21-module-structure) + - [2.2 — Function Declarations](#22-function-declarations) + - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function) + - [2.4 — Data Function Pattern](#24-data-function-pattern) + - [2.5 — Operator tensor types](#25-operator-tensor-types) +- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization) + - [3.1 — Module Structure](#31-module-structure) + - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions) + - [3.3 — Main operator function](#33-main-operator-function) + - [3.4 — Invariants](#34-invariants) +- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies) + - [4.1 — Scope Resolution](#41-scope-resolution) + - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints) + - [4.3 — How to Debug](#43-how-to-debug) + + + +
+ +--- + +
+> (Jean-Loup) I think that the use of Why3 shall be recomended from the start and that some usefull links shall be provided for readers not familiar with Why3. + +# Part 1 — Formalization Styles + +In SONNX, every operator is formalized at **two distinct levels**. + +> (Jean-Loup) "is" or "must be" or "should be"? +> +> (Jean-Loup) To name the levels ": Abstract formalization and concrete formalization." + +Each level serves a different purpose and follows a different style. + +Understanding both is essential before writing any specification. + +## 1.1 Abstract Formalization + +At this level the operator must be specified based only on its behavior and **mathematical semantics**. + +It should the **as close as possible transalation** of the mathematical definition of the operator - **presented in the informal specification**, being therefore easy to read and understand. + +No implementation details should be present at this level, which means that the **tensor' representation** should **mimic the mathematical definition of a tensor** (also similar to the ONNX tensor concept) - a multidimensional array - in which each entry is accessed by its coordinates. + +### Tensor + +- **Type**: At this level tensors are said to be **polymorphic**.
+ This means that the specifier should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types). + + > (Jean-Loup) I have no idea of what a specifier is. Depending on the targeted reader the term should be defined. + +- **Representation**: Tensors are represented as structures containing the following records: + + - `dims` - List of integers representing the shape + + - `data` - A map from coordinates to values + + > (Jean-Loup) May be valid coordinates or in-bounds coordinates instead of just coordinates. "coordinates" seems to be equal to "tensor index". If it is the case, a link to tensor index should be added. + + - `background` - The default value for out-of-bounds coordinates + +- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representatio: + + > (Jean-Loup) "representatio" <- "representation" + + 1. **Positive Dimensions** + + All dimensions in the `dims` list of a tensor must be positive integers.
+ From our point of view this will be updated to support **non-negative integers**, therefore allowing for tensors with null dimensions. [***Work in Progress***] + + **Definition:** + + - Predicate `positive` + + ```why3 + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + ``` + - `invariant { positive dims }` + + > (Jean-Loup) Indicate this is Why3 code in the text. Looking at https://why3.gitlabpages.inria.fr/why3/syntaxref.html , I can't find a definition of Cons and Nil (Even if I intuitively understand...). There is only list and forest examples using Cons and Nil. Are Cons and Nil defined by other predicates? May be the begining of the guidelines should indicate that Why3 should be used and that the reader should first be familiarized with Why3. + + 2. **Valid values** + + Coordinates are either valid for the tensor shape or the value mapped by those coordinates is the `background` value. + + **Definition:** + + - Predicate `valid` + + ```why3 + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + ``` + - `invariant { forall k. valid k dims \/ data k = background }` + +
+ + These invariants are extremely important as they will sometimes define how the operator should be formalized. + + For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant. + + > (Jean-Loup) Are the predicates positive and valid in a Why3 library? If it is the case, inclusion of the library should be recomended to avoid copy errors. + +### Specification Style + +Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output). + +Looping constructs are forbidden at this level, and instead, **recursive functions** and **mathematical constructs** should be used to define the behavior of the operator. + +> (Jean-Loup) The concept of mathematical construct should be defined. + +### Importance of this level + +It establishes the logical and mathematical specification of the operator, which serves as the **source of truth** for correctness. + +It also provides the perfect link between the informal specification and the formalization. + +## 1.2 Concrete Formalization + +The concrete level describes the **imperative implementation** that will be extracted to C code. + +It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory. + +### Tensor + +- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc..., and the operator should be defined specifically for that type. + +- **Representation**: Tensors are represented as pointers to structures that contain: + + - `t_rank`: Number of dimensions + + - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array + + - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...) + +
+ +Note that, unlike the previous level, here the tensors are defined based on a concrete target representation - tensors are essentially flat arrays in memory. + +> (Jean-Loup) I guess it depends on what is meant by "concrete representation". Asking to ChatGPT the first answer is "yes lists and maps are concrete data structures". May be it should be "target C representation" or "C built in concrete representation". + +Moreover, there is no `background` value in this representation - **tensors entries must be accessed only in valid coordinates** (i.e., coordinates that are within the bounds of the tensor shape). + +### Specification Style + +Throughout this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation. + +### Importance of this level + +It provides the necessary details to extract executable C code and reason about memory, bounds, and machine integers. + +> (Jean-Loup) I think the information in sub-sections "Importance of this level" for abstract and concrete representations should be given at the begining, i.e. before 1.1. + +## 1.3 Link between the two levels + +### Why is it important to have two levels? + +- We need somehow to express that the **concrete implementation** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**. + +- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification. + +- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes. + +- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification. + +The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement: + + + +

+ Refinement Mapping Image +

+ +The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**. + +Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections. + +--- +
+ +# Part 2 — Guidelines for Abstract Formalization + +## 2.1. Module Structure + +Each abstract module should follow this general structure: + + +``` +module OP + (* 1. Imports *) + (* 2. Auxiliary predicates *) + (* 3. Auxiliary helper functions *) + (* 4. Dimension-computing function *) + (* 5. Data-computing function *) + (* 6. Main operator function *) +end +``` + +Neither all the above sections are mandatory nor its order is fixed, however, it is recommended to follow this structure as much as possible for consistency and readability across different operator specifications. + +On the other hand, **4**, **5** and **6** are usually written in such order as each one of them depends on the previous one. + + +## 2.2 Function Declarations + +Why3 supports several ways to declare functions, each with different purposes and objectives. + +We can have any of the following function signatures: + +- `function` + +- `let` + +- `let function` + +- `let ghost function` + +- `let rec function` + +- `let rec ghost function` + +- more information on this in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html) + +Ideally, according to Loïc's proposal, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used. + +> (Jean-Loup) To remove "according to Loïc's proposal". May be describe shortly the proposal. +> +> (Jean-Loup) I don't understand why an abstract formalization of a specification cannot be a predicate using "Cons" + an invariant. + +However, this happens to be not always possible. + +### 2.2.1 Termination and Variants + +In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$. + +```why3 +module OPSummation + use int.Int + + function summation (i : int) (iter : int) : int = + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` +In this case, if you attempt to open the why3 ide the following error will be shown: + +![Cannot prove termination error](./imgs/termination.png) + +In fact, `functions` are too weak to successfully prove recursion termination and therefore we need to help them prove termination. + +To do so, we have to introduce a **variant** (some expression that repeatedly decreases with each recursive call). + +However, `function` **does not support contracts** and therefore **variants** are **forbidden**, so we need to switch to `let rec function` and provide the variant there. + +```why3 + let rec function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` + +> (Jean-Loup) In the why3 Langage Reference it is written "Recursive program functions must be defined using let rec." and some examples don't use the keyword "function". Is it possible to have only "let rec summation"? Is there a difference between "let rec" and "let rec function"? + +### 2.2.2 Verification Conditions and Requires Clauses + +Note that, functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**. + +Changing the signature from `function` to `let rec function` will, on the other hand, generate such verification conditions. + +That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions. + +To compare such different behaviors, check the examples below: + +**Explicitly need for requires clauses** + +```why3 + (** Helper function to extract element from list at given position **) + + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end +``` + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) +``` + +This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list. + +Consequently, verification conditions will be raised as is depicted in the picture below: + +![Flatten abstract Verification Conditions - not proved](./imgs/flatten_VC_not_proved.png) + +In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions. + +Therefore, the code above should be adapted to include such contracts: + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + (...) +``` + +![Flatten abstract Verification Conditions - proved](./imgs/flatten_VC_proved.png) + +**No need for requires clauses** + +```why3 + function calculate_dims (x : tensor real) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) +``` +
+ +The code written above will not generate any verification conditions, even if the functions called inside it explicitly need **requires clauses**. + +Therefore, no verification condtions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left pannel in the picture above). + +### 2.2.3 TypeInvariant Lemmas + +When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form. + +As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties. + +The `calculate_dY0` function defined above is a concrete example of this issue. + +The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive: + +```why3 + let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) +``` + +The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints). + +### 2.2.4 Main Operator Function + +Moreover, the main operator function cannot be declared as `function` because it needs to have contracts (`requires` / `ensures`) and `function` does not support contracts. + +Therefore, the main operator function should be declared as `let ghost function` with full contracts. + +```why3 +module OPSummation + use int.Int + + let rec function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 + + let ghost function unitSum (n: int) : int = + requires { n >= 0 } + ensures { result = n } + summation 0 n +end +``` + +### 2.2.5 Guidelines + +| Guideline | Details | +|:---|:---| +| Use `function` for total functions and whenever it is possible! | A `function` is a logical function. | +| Use `let rec function` only when recursion needs a variant. | If Why3 cannot prove termination of a `function`, switch to `let rec function` and provide a `variant` clause. | +| Use `let ghost function` for the **main operator**. | The main function should be a `let ghost function` with full contracts (`requires` / `ensures`). | + +### Examples + +Besides the summation example above, here are some examples of such formalization styles: + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + +## 2.3. Contracts on the Main Function + +Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec`. + +The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions: + +| Postcondition | Purpose | +|:---|:---| +| `ensures { result.dims = ... }` | Specifies the **output shape** | +| `ensures { result.data = ... }` | Specifies the **output data** (as a map) | +| `ensures { result.background = ... }` | Specifies the **background value** | + +### Example + +```why3 +let ghost function opclip (x l m : tensor real) : tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result.dims = x.dims } (* ← shape *) + ensures { result.data = dclip x l m } (* ← data *) + ensures { result.background = x.background } (* ← background *) += + { dims = x.dims; data = dclip x l m; background = x.background } +``` + +## 2.4. Data Function Pattern + +Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern: + + +### Anonymous function declaration + +Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics. + +We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function. + +Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**. + +That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal). + +```why3 +let ghost function d (x: tensor a') (output_shape: list int) : data a' += +fun ks -> + if valid ks output_shape then + ... + else + +``` + +The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics. + +The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics. + +This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor. + +### Recursive dimensions constructs + +An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions. + +This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof. + +As a standard we will stick with the `anonymous function` definition. + +### Examples + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + + +## 2.5 Operator tensor types + +Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level. + +For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors. + +However, there are operator whose abstract tensor type can be completely polymorphic. + +Take a look ate the example below: + +```why3 +module OPWhere + use tensor.Tensor + + function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) +end +``` +
+ +Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`. + +Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**. + +Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic. + +So whenever possible one should resort to **polymorphic** abstract datatypes. + +This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others. + + +# Part 3 — Guidelines for Concrete Formalization + +## 3.1. Module Structure + +The concrete module should follow this general structure: + +```why3 + module COP + (* 1. Imports *) + (* 2. Auxiliary helper functions *) + (* 3. Main operator function *) + end +``` + +### 3.2 Auxiliary helper functions + +All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well. + +Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ... + +Under this context, translation means: + +- Defining the function in an imperative style + +- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping) + +To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw): + +```why3 + (* Abstract level *) + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 +``` + +```why3 + (* Concrete level *) + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end +``` + +These two functions perform the same exact computation. + +To better understand how these two relate one can compare both specifications: + +**Coordinates** + +- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator. + + - These functions are equivalent and such equivalence proof is already available in the tensors library + +- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps: + + - **Firstly**, the appropriate space is allocated with the `malloc` function + + - **If the malloc is successful**, we then set each index of the array with the appropriate value. + + - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause. + +**Computation** + +- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0. + +- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0. + +- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels. + +**Refinement mapping** + +- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by: + + ``` + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + ``` + +- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels. + +- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created. + +### 3.3 Main operator function + +This is naturally the most important function and the one whith more details to be taken into account. + +#### 3.3.1 Contracts + +The **main operator function** must include contracts at both levels. + +**Requires** + +- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +- On the **concrete level** we should have preconditions for: + + 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor** + + 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**. + + 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change). + +**Ensures** + +- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**: + +- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form: + + - `ensures { tensor result = (tensor x) ... }` + +The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw). + +- **Concrete level** + + ```why3 + let flatten (x r : ctensor) (axis: int32) + (* Requires*) + (* 1 - Valid tensors *) + requires { valid_tensor x } + requires { valid_tensor r } + + (* 2 - Shape match *) + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + + (* 3 - Informal Spec *) + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + + (* Ensures *) + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + + assert { tensor r == flatten (tensor x) (to_int axis) } + ``` + +**Requires** + +Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors. + +Expressing the **shape match** on the other hand is not that trivial. +There are multiple ways of doing this match. +The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same. +To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`. + +- `tensor`: Converts a **ctensor** into an **abstract tensor** + +- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int` + +- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important. + + - Takes as inputs: the **pointer** to convert and the **desired size** of conversion + +For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level. + +Translating the preconditions from the abstract level to the concrete level can be done in two different ways: + + 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions + + 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure. + +In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provavle than the other, so it is up to the specifier to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms: + +- `requires { size (tensor x) = size (tensor r) }` + +To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved. + +This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them. + +Check the following example: + +```why3 + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let ghost _ = flatten (tensor x) (to_int axis) in + (...) +``` + +This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator. + +The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved + +![Debugging VCs 0](./imgs/flatten_requires.png) + +If one had forgotten to include this precondition: + +``` +requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } +``` + +then, the strategy proposed above would have generated the following verification condition which is not automatically proved: + +![Debugging VCs 1](./imgs/flatten_requires_1.png) + +By inspecting the task menu, the specifier would be able to understand which precondition is missing and add it to the specification. + + + +**Ensures** + +Our final goal is to prove the **ensures** clause stating the refinement between both levels. + +Usually the following pipeline is followed to do so: + +$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$ + +Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof. + +### 3.4 Invariants + +**Nested Loop Invariants** + +Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**. + +A loop invariant must satisfy two proof obligations: + +1. **Initialization** — the invariant holds for the first iteration. + +2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive. + +When loops are nested, these obligations form a dependency chain: + +- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant + +- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant. + +The following example illustrates this: + +```why3 +let ref sum = (f32 0.0) in +(* ... *) +for i = 0 to rows - 1 do + invariant { sum = 0.0 } + for j = 0 to cols - 1 do + invariant { sum = 0.0 } + for k = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) i j 0 k } + (* ... *) + done; + sum <- 0.0; + done; +done; +``` + +The innermost loop (`k`) accumulates a dot product in `sum`. + +For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore: + +$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$ + +In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$. + +### Loop Invariants for Proving Data Refinement + +**Example 1 - Unit Summation** + +To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range. + +The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one. + +The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove: + +![Summation Invariant Preservation](./imgs/summation_invariant0.png) + +The goal and the respective logical context are below: + +![Summation Invariant Preservation](./imgs/summation_invariant1.png) + +What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration. + +To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level. + +Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1: + +```why3 + let rec lemma summation_lemma (i iter : int) + requires { i < iter } + ensures { 1 + summation (i + 1) iter = summation i iter } + variant { iter - i } + = + if i >= iter then + () + else + summation_lemma (i + 1) iter +``` + +After adding this, the invariant preservation is easily proved. + +This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation. + +**Example 2 - Dot Product** + +The example presented above is a very simple one, but the same strategy can be applied to much more complex operators. + +Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator. + +The file is available [here](./examples/dot_product.mlw). + +Once again the proof is not achievable and the task menu displays the following: + +![Dot Product Invariant Preservation](./imgs/dot_product0.png) + +To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma. + +The strategy is essentially the same: + +```why3 + let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int) + requires { Int.(i <= k < iter) } + variant { Int.(k - i) } + ensures { + let a_val = a.data (Cons row (Cons k Nil)) in + let b_val = b.data (Cons k (Cons col Nil)) in + dot_product_rec a b row col i Int.(k + 1) = + Real.(dot_product_rec a b row col i k + a_val * b_val) + } += + if i < k then + Real.(dot_product_lemma a b row col (i + 1) k iter) + else + () +``` + +This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma. + +![Dot Product Invariant Preservation](./imgs/dot_product1.png) + +To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables. + +We will do so by introducing a ghost block: + +```why3 +ghost begin + dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter) +end +``` + +After this, the invariant preservation is easily proved. + +More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints). + +**Convolution** + +The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops. + +Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates. + +Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above. + +The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration. + +This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate. + +The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement. + +Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor. + +Below are the four loop invariants used in the concrete Conv implementation: + +```why3 + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; +``` + +The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically. + +The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected. + +They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten. + +This must be asserted explicitly each time. + +### The innermost loop + +The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop): + +```why3 + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } +``` + +The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct: + +```why3 + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; +``` + +With these assertions in place, the proof of the innermost invariant succeeds. + +### The outer loops + +The preservation of the outer invariants still fails. + +To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant: + +```why3 +constant yy_hh : int + +H3 : 0 <=' yy_hh + +H2 : yy_hh <' (y_hh +' 1) + +constant yy_ww : int + +H1 : 0 <=' yy_ww + +H : yy_ww <' int32'int y_w + +constant coords : list int = + Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int)))) + +constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank)) + +------------------------------- Goal -------------------------------- + +goal c_conv'vc : + (value_at (r.t_data) @ offset1 coords dims).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h) + (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left) +``` + +Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant: + +``` +LoopInvariant2 : + forall yy_hh1:int. + 0 <=' yy_hh1 /\ yy_hh1 <' y_hh -> + (forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w -> + (let coords'unused = + Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r1.t_data) + @ offset1 + (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))) + (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 + (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3)) + nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w) + (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left))) +``` + +At first glance, the hypothesis and the goal look nearly identical. + +However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`. + +The only relationship between `r` and `r1` available in the logical context is: + +```why3 +H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data) +``` + +Meanwhile, from the innermost loop invariant, the following hypothesis is also available: + +``` +LoopInvariant1 : + forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) -> + (let coords'unused = + Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r.t_data) + @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))) + (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h) + (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left)) +``` + +The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices. + +This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop. + +The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant: + +``` +for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + +``` + +By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant. + +However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop. + +The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten. + +The same pattern of assertions resolves this: + +```why3 + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; +``` + +Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator. + +The completed file is available [here](./examples/conv.mlw). + +# Part 4 - Tips, Hints and Strategies + +## 4.1. Scope Resolution + +When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors. + +Take a look at the following example: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { iter - i } + = + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 +end +``` + +If you attempt to open the Why3 ide with the above code, you will get the following error: + +![Type error due to scope resolution](./imgs/scope.png) + +This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`. + +In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name. + +In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules. + +Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope. + +To solve the issue we will have to specify the scope operations on the `Int` domain: + + +$$(iter - i) \rightarrow Int.(iter - i)$$ + +$$i < iter \rightarrow Int.(i < iter)$$ + +$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$ + +The corrections applied to the code above are the following: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { Int.(iter - i) } + = + if Int.(i < iter) then + 1.0 + summation Int.(i + 1) iter + else + 0.0 +end +``` + +## 4.2. IDE Transformations and Prover Hints + +Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals. + +Below are techniques we have found useful in practice. + +The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE. + +--- + +### Lemma / Axiom Instantiation + +When an axiom or lemma is stated universally, for example: + +```why3 +axiom test: + forall x: int. P(x) -> Q(x) +``` + +The automated prover may either fail or take significantly longer to apply it in a specific context. + +Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful. + +**Example — Mean Value Theorem:** + +Consider the following axiom: + +```why3 +(* Lagrange Mean Value Theorem *) +axiom mean_value_theorem_sigmoid: + forall x y: real. + exists c: real. + (x <= c <= y \/ y <= c <= x) /\ + sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y) +``` + +And the following lemma to be proved: + +```why3 +lemma lipschitz_sigmoid: + forall x y: real. + abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y) +``` + +To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem. + +Instantiating the axiom with the specific variables `x` and `y` greatly helps. + +**IDE instantiation syntax:** + +``` +instantiate +``` + +If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step. + +The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it. + +This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE. + +--- + +### Instantiation via Lemma Functions + +Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved. + +This is the preferred approach, as it keeps the proof self-contained and reproducible. + +**Example — `valid_bounds_2` lemma function:** + +The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions: + +```why3 +let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end +``` + +Now consider the `dot_product` function, which allocates coordinate arrays at runtime: + +```why3 +let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) = + requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter } + requires { a.t_rank = b.t_rank = 2 } + requires { iter >= 0 } + requires { row >= 0 /\ row < value_at a.t_dims 0 } + requires { col >= 0 /\ col < value_at b.t_dims 1 } + (* ... *) + let ref sum = (f32 0.0) in + let ref flag = False in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + if is_not_null a_coords_array && is_not_null b_coords_array then begin + flag <- True; + for i = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) row col 0 i } + set_ofs a_coords_array 0 row; + set_ofs a_coords_array 1 i; + set_ofs b_coords_array 0 i; + set_ofs b_coords_array 1 col; + + ghost valid_bounds_2 a_coords_array a.t_dims 0 2; + (* Rest of code ... *) +``` + +Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own. + +Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue. + +--- + +### Function Unfolding + +When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle. + +Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE. + +**`unfold`** expands a specific function by its definition at the point where it appears in the goal. + +To use `unfold` transformation, enter the following command in the **"Type command here"** field: +``` +unfold +``` + +Before applying `unfold`: + +![before unfold](./imgs/before_unfold.png) + +After applying `unfold`: + +![after unfold](./imgs/after_unfold.png) + +**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously. + +It requires no arguments: + +``` +compute_in_goal +``` + +Result after applying `compute_in_goal`: + +![compute_in_goal](./imgs/compute_in_goal.png) + + +--- + +## 4.3. How to Debug + +"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal. + +There are two common root causes: + +1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated. + +2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces. + +The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap. + +### Reading the Logical Context + +Consider the following goal: + +``` +------------------------------- Goal -------------------------------- + +goal w_cools_calculate'vc : + to_extended_real max_value = + w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b) + (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) + (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom) + (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w) +``` + +In the logical context, among other hypotheses, we have: + +``` +constant x_val : float + +constant aux_flag : bool + +Ensures4 : + aux_flag = True -> + coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) + (to_int2 pad_left) = to_extended_real x_val + +constant ww1 : int321 + +constant ww : int = int32'int ww1 + +H31 : 0 <=' ww + +H30 : ww <=' int32'int o2 + +LoopInvariant : + to_extended_real max_value1 = + w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c) + (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h) + (to_int2 str_w) + +H1 : + w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0 + (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h) + (int32'int dil_w) (int32'int pad_top) (int32'int pad_left) + (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h) + (int32'int str_w) = + max_extended_real + (coords_from_X_p (tensor x) + (Cons (int32'int b) + (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int))))) + (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) + ...) + +Ensures : + to_extended_real (max max_value1 x_val) = + max_extended_real (to_extended_real max_value1) (to_extended_real x_val) + +constant max_value : float + +H : max_value = max max_value1 x_val +``` + +The logical context is sufficient to prove the goal. Here is the reasoning: + +**Left-hand side of the goal.** + +From `H`, we know `max_value = max max_value1 x_val`, so: + +``` +to_extended_real max_value = to_extended_real (max max_value1 x_val) +``` + +Applying `Ensures`: + +``` += max_extended_real (to_extended_real max_value1) (to_extended_real x_val) +``` + +**Right-hand side of the goal.** + +Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`: + +``` +w_cools_calculate ... (ww +' 1) ... + = max_extended_real + (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...) +``` + +From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`. + +For the first argument, `Ensures4` tells us (given `aux_flag = True`) that: + +``` +coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left) + = to_extended_real x_val +``` + +So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity). + +However, Why3 cannot discharge the goal automatically. + +**Why Why3 doesn't make the proof** + +The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor: + +``` +coords_from_X_p (tensor x) + (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) + (int32'int pad_top) (int32'int pad_left) +``` + +whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`. + +These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own. + +The connection must be made explicit with an intermediate assertion: + +```why3 +assert { ivector x_p_coords_array 4 = + Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) }; +``` + +This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through. diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md new file mode 100644 index 00000000..d018ea89 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/formal/formal_guidelines.md @@ -0,0 +1,1571 @@ +# SONNX — Formalization Guidelines + +A structured guide for writing formal specifications of SONNX operators in Why3. + +This guidelines do not cover the basics of Why3, so familiarity with its syntax, semantics and features is expected. + +--- + +## Table of Contents + +- [Part 1 — Formalization Styles](#part-1--formalization-styles) + - [1.1 — Abstract Formalization](#11-abstract-formalization) + - [1.2 — Concrete Formalization](#12-concrete-formalization) + - [1.3 — Link between the two levels](#13-link-between-the-two-levels) +- [Part 2 — Guidelines for Abstract Formalization](#part-2--guidelines-for-abstract-formalization) + - [2.1 — Module Structure](#21-module-structure) + - [2.2 — Function Declarations](#22-function-declarations) + - [2.3 — Contracts on the Main Function](#23-contracts-on-the-main-function) + - [2.4 — Data Function Pattern](#24-data-function-pattern) + - [2.5 — Operator tensor types](#25-operator-tensor-types) +- [Part 3 — Guidelines for Concrete Formalization](#part-3--guidelines-for-concrete-formalization) + - [3.1 — Module Structure](#31-module-structure) + - [3.2 — Auxiliary helper functions](#32-auxiliary-helper-functions) + - [3.3 — Main operator function](#33-main-operator-function) + - [3.4 — Invariants](#34-invariants) +- [Part 4 — Tips, Hints and Strategies](#part-4---tips-hints-and-strategies) + - [4.1 — Scope Resolution](#41-scope-resolution) + - [4.2 — IDE Transformations and Prover Hints](#42-ide-transformations-and-prover-hints) + - [4.3 — How to Debug](#43-how-to-debug) + + + +
+ +--- + +
+ +# Part 1 — Formalization Styles + +In SONNX, every operator must be formalized at two distinct levels: abstract formalization and concrete formalization. + +Each level serves a different purpose and follows a different style. + +We start from an abstract formalization — which captures the mathematical semantics of the operator and serves as the **source of truth** for correctness — and progressively refine it into a concrete formalization that is close enough to the target implementation to be automatically extracted into C code - reasoning about memory, bounds, and machine data types. + +Understanding both is essential before writing any specification. + +## 1.1 Abstract Formalization + +At this level the operator must be specified making no reference to any implementation detail, and therefore it should be **implementation-agnostic**. + +It shall be **traceable** to the **informal specification**, being easy to validate, first and easy to read and understand, second. Traceability must be enforced: + +- first by keeping the formal specification as close to the informal specification (e.g., stick to the mathematical specification given in the informal specification) + +- second by providing explicit link via `[tags]` to specific parts of the informal specification. + +No implementation details should be present at this level, which means that the specification of tensors shall remain as abstract as possible, focusing on their mathematical properties. + +### Tensors + +- **Type**: At this level tensors are specified as **polymorphic structures**, i.e., they are parameterized by the type of the values they contain.
+ This means that the specification author should be as generic as possible when defining an operator.
+ More details in - [2.5 - Operator tensor types](#25-operator-tensor-types). + +- **Representation**: Tensors are represented as structures containing the following records: + + - `dims` - List of integers representing the shape + + - `data` - A map from [indexes](../../../spec/informal/common/definitions.md) to values + + - `background` - The default value for indexes out of the bounds defined by the shape + + A "valid index" is an index compatible with the dimension of the tensor, i.e. the i-th components of the index is strictly positive and strictly less than the i-th dimension of the tensor + + The data mapping is complete since it maps all possible indexes to values, but the value mapped by invalid indexes is the `background` value. + +- **Type Invariants**: There are two important invariants that must be satisfied by this tensor representation: + + 1. **Positive Dimensions** + + All dimensions in the `dims` list of a tensor must be **strictly positive integers**.
+ This constraint may be relaxed in a lated version of the specification to support tensors with **null** dimensions. [***Work in Progress***] + + - Predicate `positive` + + ```why3 + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + ``` + - `invariant { positive dims }` + + 2. **Valid values** + + Indexes are either valid for the tensor shape or the value mapped by those indexes is the `background` value. + + - Predicate `valid` + + ```why3 + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + ``` + - `invariant { forall k. valid k dims \/ data k = background }` + +
+ + These invariants are extremely important as they will sometimes define how the operator should be formalized. + + For example, while computing the data of the tensor it is **highly recommended** to use the predicate `valid`, essentially to capture and pass the invariant. + + An alternative was to define the data through recursive definitions once the shape is already computed, but this approach is much more complex and usually requires auxiliary lemmas to help the proof. + + The above predicates and invariants are available in the [tensors library](../../../spec/formal/common/libs/tensor/). + +### Specification Style + +Throughout this level, the specification should be carried out in a **purely functional style** meaning that there should be no side effects and no mutable state (functions do not modify state, they receive one as input and return a new one as output). + +Users should specify the operator based on mathematically and recursive definitions, avoiding loops and mutable state, increasing the **provability** and **ease of proof** of this specification. + +## 1.2 Concrete Formalization + +The concrete level describes the **imperative implementation** that will be extracted to C code. + +It describes it based on a **target C representation** for tensors called **ctensors** which are essentially structures backed by flat arrays in memory. + +### Tensors + +- **Type**: At this level the type of tensor elements is fixed to be `float32`, `float64`, `int32`, `int64`, etc. and the operator should be defined specifically for that type. + +- **Representation**: Tensors are represented as pointers to structures that contain: + + - `t_rank`: Number of dimensions + + - `t_dims`: Pointer to a flat array containing the tensor dimensions - pointer to `int32` array + + - `t_data`: Pointer to a flat array containing the tensor elements - pointer to `type` array (where type is `float32`, `float64`, `int32`, `int64`, ...) + +
+ +Note that, unlike the previous level, here the tensors are defined based on a target C representation - tensors are essentially flat arrays in memory. + +Moreover, there is no `background` value in this representation - **only valid indexes must be used to access values of tensors** (i.e. coordinates that are within the bounds of the tensor shape). + +### Specification Style + +In this level, the specification should be carried out in an **imperative style** meaning that **loops and mutable state** are allowed and often necessary to express the implementation. + +### Importance of this level + +It provides the necessary details to extract executable C code and reason about memory, bounds, and machine data types. + +## 1.3 Link between the two levels + +### Why is it important to have two levels? + +- We need somehow to express that the **concrete specification** is correct with respect to the **operator's intended behavior** - which is captured by the **abstract specification**. + +- The abstract spec is easy to **read, understand, and reason about** — it directly mirrors the mathematical definition from its informal specification. + +- The concrete spec is necessary to **extract executable C code** and reason about memory, bounds, and machine datatypes. + +- The two levels are connected through a **refinement relationship**. The concrete implementation must be proven to produce the same result as the abstract specification. + +The following image gives a high-level overview of the relationship between the two levels and how they connect through refinement: + + + +

+ Refinement Mapping Image +

+ +The refinement relationship is expressed through a **postcondition** in the concrete implementation that states that **the result of the concrete implementation** (after converting it to an abstract tensor) must be **equal** to the **result of the abstract specification**. + +Such relation will be explored in [3.2](#32-auxiliary-helper-functions) and [3.3](#33-main-operator-function) sections. + +--- +
+ +# Part 2 — Guidelines for Abstract Formalization + +## 2.1. Module Structure + +Each abstract module should follow this general structure: + + +``` +module OP + (* 1. Imports *) + (* 2. Auxiliary predicates *) + (* 3. Auxiliary helper functions *) + (* 4. Dimension-computing function *) + (* 5. Data-computing function *) + (* 6. Main operator function *) +end +``` + +If any section is not needed, it can be left empty, but the overall structure must be maintained for consistency and readability across different operator specifications. + +## 2.2 Function Declarations + +Why3 supports several ways to declare functions, each with different purposes and objectives. + +In order to fully understand each one of this features please take into account that whyml supports **two different programming namespaces**, a **logical** and a **programming** one, each one of them built upon a different syntax and with different features. For instance both of them support **conjunctions** but while the logical one expresses it through $\land$, the programming one expresses it through the `&&` operator. + + +We can have any of the following function signatures: + +- `function`: Belongs to the **logical namespace** and is used to define purely logical functions. Contracts are not supported. + +- `let`: Belongs to the **programming namespace** and defines a non recursive function. + +- `let rec`: Belongs to the **programming namespace** and defines a recursive function. + +- `let function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and allows them to be used at the logical namespace as well. + +- `let rec function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and allows them to be used at the logical namespace as well. + +- `let ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **non recursive** and **can only** be used at the **logical namespace**. + +- `let rec ghost function`: Belongs to the **programming namespace**, however, it represents **pure functions**, that are **recursive** and **can only** be used at the **logical namespace**. + +- A precise definition of these constructs is given in the [Why3 manual, section 6.5.5](https://why3.org/doc/syntaxref.html) + +Ideally, **at the abstract level** we should only declare function with the signature `function` and no contracts (`requires` / `ensures`) for auxiliary functions should be used. + +> Recall why this is considered as good practice. + +### 2.2.1 Termination and Variants + +It is not always possible to define all the necessary functions with the `function` construct, especially when we need to define recursive functions whose termination is not trivially provable by Why3. + +In order to understand that have a look at the following module which computes the unit summation in the range $[0, n-1]$. This might not be the most traditional way of computing a summation, although, it intentionally resembles the iterative way of computing a summation, starting from 0 up to $n-1$. + +```why3 +module OPSummation + use int.Int + + function summation (i : int) (iter : int) : int = + if i < iter then + 1 + summation (i + 1) iter + else + 0 +end +``` +In this case, if you attempt to open the why3 ide the following error will be shown: + +![Cannot prove termination error](./imgs/termination.png) + +In fact, `functions` are not supposed to be used in recursive definitions, unless their termination is trivially provable by Why3 such as recursive calls over the tail of a list. + +Intead one should use a `let rec ghost function` declaration, which allows to define recursive functions with a **variant** (some expression that repeatedly decreases with each recursive call) that can only be used in the logical namespace. + +```why3 + let rec ghost function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 +``` + +### 2.2.2 Verification Conditions and Requires Clauses + +Note that, pure logical functions declared by the keyword `function` never generate verification conditions, even if some of the functions called inside it need to hold some **pre-conditions**. + +Recalling to the [definitions](#functions_def) it is clear that `function` and `let rec ghost function` belong to different namespaces, although both of them can only be used at the logical namespace. As a consequence, since `let rec ghost function` is not a definition but an actual executable function, it generates verification conditions for all the functions called inside it that have preconditions. + +That's why, under such circumstances, requires clauses can be added **only** to properly prove such verification conditions. + +To compare such different behaviors, check the examples below: + +**Explicitly need for requires clauses** + +```why3 + (** Helper function to extract element from list at given position **) + + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end +``` + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) +``` + +This piece of code - from the [flatten formalization](./examples/flatten.mlw) - will generate verification condition because the function is signed with `let rec function` and `get_dims`, which is called inside it, has **preconditions** stating that the value being accessed is valid within the list. + +Consequently, verification conditions will be generated as is depicted in the picture below: + +![Flatten abstract Verification Conditions - not proved](./imgs/flatten_VC_not_proved.png) + +In such cases we need explicitly to provide all the requires clauses raised by the verification conditions of the called functions. + +Therefore, the code above should be adapted to include such contracts: + +```why3 + let rec ghost function calculate_dY0 (x : tensor real) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + (...) +``` + +![Flatten abstract Verification Conditions - proved](./imgs/flatten_VC_proved.png) + +**No need for requires clauses** + +```why3 + function calculate_dims (x : tensor real) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) +``` +
+ +The code written above will not generate any verification conditions, even if the functions referenced inside it explicitly need **requires clauses**. + +Therefore, no verification conditions were raised and the function `calculate_dims` does not appear on the Verification Conditions menu (left panel in the picture above). + +### 2.2.3 TypeInvariant Lemmas + +When using `let rec function` declarations, their logical context sometimes does not propagate to subsequent functions, or it may appear only in axiomatic form. + +As a consequence, proving the type invariants of tensors — in particular, ensuring that the output dimensions satisfy the positivity invariant — requires explicit lemmas that establish these properties. + +The `calculate_dY0` function defined above is a concrete example of this issue. + +The following lemma is needed to guarantee that the result produced by `calculate_dY0` is strictly positive: + +```why3 + let rec lemma calculate_dY0_positive (x: tensor real) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) +``` + +The different kinds of lemmas and how to use them will be covered in section [3.4](#34-invariants), [4.2](#42-ide-transformations-and-prover-hints). + +### 2.2.4 Main Operator Function + +To enforce traceability, as [previously mentioned](#traceability), the main operator function should capture the constraints present in the informal specification as preconditions (`requires`) while providing postconditions (`ensures`) for every record of the output tensor. + +To capture such contracts one needs to define a construct that belongs to the programming namespace. Moreover, since this function is only used to specification purposes it is not supposed to be used in any implementation context. To enforce that this function can only be used at the logical level sign it as **ghost**. + +Therefore, the main operator function should be declared as `let rec ghost function` with full contracts. + +```why3 +module OPSummation + use int.Int + + let rec ghost function summation (i : int) (iter : int) : int = + variant { iter - i} + if i < iter then + 1 + summation (i + 1) iter + else + 0 + + let ghost function unitSum (n: int) : int = + requires { n >= 0 } + ensures { result = n } + summation 0 n +end +``` + +### Examples + +Besides the summation example above, here are some examples of such formalization styles: + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + +## 2.3. Contracts on the Main Function + +Until now, we have stated that no function should have contracts except for the `main function` or any other function declared with `let rec ghost function`. + +The `main function`, **must** include as preconditions (`requires`) all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +On the other hand, the postconditions (`ensures`) of the main operator function **must** include the following three postconditions: + +| Postcondition | Purpose | +|:---|:---| +| `ensures { result.dims = ... }` | Specifies the **output shape** | +| `ensures { result.data = ... }` | Specifies the **output data** (as a map) | +| `ensures { result.background = ... }` | Specifies the **background value** | + +### Example + +```why3 +let ghost function opclip (x l m : tensor real) : tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result.dims = x.dims } (* ← shape *) + ensures { result.data = dclip x l m } (* ← data *) + ensures { result.background = x.background } (* ← background *) += + { dims = x.dims; data = dclip x l m; background = x.background } +``` + +## 2.4. Data Function Pattern + +Whenever we want to specify the operator along all possible coordinates we can follow any of the following pattern: + + +### Anonymous function declaration + +Create an **anonymous function** for the data that takes the coordinates as input and defines the value at those coordinates based on the operator's semantics. + +We are basically stating that for all possible coordinates, the value at those coordinates is defined by this function. + +Note that, **not all coordinates are valid**, so we need to **check the validity of the coordinates** first and **return the background value for invalid coordinates**. + +That's why we need to compute the output shape, prior to this computation, as proposed in the [previous section](#file-structure-proposal). + +```why3 +let ghost function d (x: tensor a') (output_shape: list int) : data a' += +fun ks -> + if valid ks output_shape then + ... + else + +``` + +The construct `fun ks -> ...` is an anonymous function that takes `ks` as input and defines the value at coordinates `ks` based on the operator's semantics. + +The condition `if valid ks output_shape then ... else ...` checks if the coordinates `ks` are valid (i.e., within the bounds of the output shape) and returns the appropriate value based on the operator's semantics. + +This construct returns a map from coordinates to values, which is exactly what we need for the `data` field of the output tensor. + +### Recursive dimensions constructs + +An alternative way would be to define as much recursive helper functions as the number of dimensions and then define the data function based on these recursive functions. + +This approach is much more complex both to write and to read and usually requires auxiliary lemmas to help the proof. + +As a standard we will stick with the `anonymous function` definition. + +### Examples + +- **Clip**: [Clip abstract specification](./examples/clip.mlw) + +- **MatMul**: [MatMul abstract specification](./examples/matmul.mlw) + + +## 2.5 Operator tensor types + +Until now, all the examples we have provided, explicitly stated the type of the tensor at the abstract level. + +For example [Clip](./examples/clip.mlw) and [Matmul](./examples/matmul.mlw) are explicitly specified for `real` tensors. + +However, there are operator whose abstract tensor type can be completely polymorphic. + +Take a look ate the example below: + +```why3 +module OPWhere + use tensor.Tensor + + function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) +end +``` +
+ +Unlike, the previous examples, this operator is defined for tensors with a polymorphic datatype `'a`. + +Once for clip and matmul we need to compute values, there is no other option but to have an abstract datatype which is in fact **numerical**. + +Nevertheless, **where** doesn´t resort to any mathematical handling whatsoever and that's why its abstract datatype can be polymorphic. + +So whenever possible one should resort to **polymorphic** abstract datatypes. + +This is the case for the vast majority of the structural operators: **flatten**, **reshape**, among others. + + +# Part 3 — Guidelines for Concrete Formalization + +## 3.1. Module Structure + +The concrete module should follow this general structure: + +```why3 + module COP + (* 1. Imports *) + (* 2. Auxiliary helper functions *) + (* 3. Main operator function *) + end +``` + +### 3.2 Auxiliary helper functions + +All the functions that are used on the **abstract side** to compute the data need to be translated to this level as well. + +Note, however, that this translation usually implies redefining the function in an imperative style - **for loops**, **memory allocation**, ... + +Under this context, translation means: + +- Defining the function in an imperative style + +- Check that the concrete level function is indeed a refinement of the respective abstract function.
+Equivalent to what is presented in the image [here](#refinement-mapping) + +To better understand this, check the `coords_from_X_p` function, presented in the [conv](./examples/conv.mlw): + +```why3 + (* Abstract level *) + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 +``` + +```why3 + (* Concrete level *) + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end +``` + +These two functions perform the same exact computation. + +To better understand how these two relate one can compare both specifications: + +**Coordinates** + +- On the **abstract side** the indices of the coordinates are captured with the `get_dim` function while on the **concrete level** the indices are captured by directly accessing the `iarray` with the `[]` operator. + + - These functions are equivalent and such equivalence proof is already available in the tensors library + +- To define the whole coordinates, on the **abstract side**, we only need a **list constructor**. On the **concrete level** such process is performed in two distinct steps: + + - **Firstly**, the appropriate space is allocated with the `malloc` function + + - **If the malloc is successful**, we then set each index of the array with the appropriate value. + + - Once again an equivalence must be established between these two constructs. That's exactly what is performed by the `assert` clause. + +**Computation** + +- On the **abstract side** we perform the computation by checking the validity of the coordinates on the dimensions of tensor x using the predicate **valid**. If the coordinates are valid we are going to return the value that is on that coordinates, else we are going to return 0. + +- On the **concrete side** we perform the computation by calculating the flatindex, of the coordinates on the tensor x dimensions, if the flatindex is a valid index, then we return the value on that index, else we are going to return 0. + +- The equivalence is captured by the postcondition present on the `coffset` function and the `if clause` which follows the same pattern at both levels. + +**Refinement mapping** + +- The refinement mapping relation is expressed with an ensures. In this function this realtion is expressed by: + + ``` + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + ``` + +- This is the most important relation between both levels, as it states the values returned by both functions are exactly the same. Ultimately this prove that the function specified at the concrete level is correct with respect to the expected one presented at the abstract level, therefore ensuring that the behavior is the same at both levels. + +- Note that, in the concrete level, we need to explicitly check that the `malloc` was successful and return a flag indicating whether the coordinates were successfully computed or not. This is not necessary on the abstract level because we are working with mathematical constructs and we can assume that such constructs are always successfully created. + +### 3.3 Main operator function + +This is naturally the most important function and the one whith more details to be taken into account. + +#### 3.3.1 Contracts + +The **main operator function** must include contracts at both levels. + +**Requires** + +- On the **abstract level** the preconditions should include `requires` for all the necessary constraints that are present in the informal specification for the inputs and the attributes. Output constraints (such as shape) will not be included at this point. + +- On the **concrete level** we should have preconditions for: + + 1. Expressing the **validity of the input tensors**. At this level the tensors are passed as arguments and therefore we need to evalute whether they are **valid tensor** + + 2. Verify if the output tensor (already passed as an argument) has the appropriate shape, computed with the abstract function with such objective. Such verification should be performed by checking the dimensions of the output tensor with the dimensions computed by the abstract function. Moreover, we should also check that the output tensor has the appropriate **rank**. + + 3. All the **preconditions** already present at the abstract level. The **concrete** will essentially have the exact same copy of the abstract preconditions but adapted to the concrete level (the way the accesses are performed for instance will change). + +**Ensures** + +- On the **abstract level** the postconditions should cover each record of the abstract tensor - **dimensions**, **data** and **background**: + +- On the **concrete level** the only needed clause is the refinement mapping, indicating that the concrete implementation is correct with respect to the abstract specification. This is usually expressed by a postcondition of the form: + + - `ensures { tensor result = (tensor x) ... }` + +The example below gives a better understanding of the above guidelines. The full file is available [here](./examples/flatten.mlw). + +- **Concrete level** + + ```why3 + let flatten (x r : ctensor) (axis: int32) + (* Requires*) + (* 1 - Valid tensors *) + requires { valid_tensor x } + requires { valid_tensor r } + + (* 2 - Shape match *) + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + + (* 3 - Informal Spec *) + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + + (* Ensures *) + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + + assert { tensor r == flatten (tensor x) (to_int axis) } + ``` + +**Requires** + +Regarding the `requires` clause, expressing the validity of the **ctensors** is really quite simple, one just needs to call the `valid_tensor` predicate with any of the operator input tensors. + +Expressing the **shape match** on the other hand is not that trivial. +There are multiple ways of doing this match. +The one we propose here is performed by literally calculating the shape with the abstract module and then check if they are the same. +To do so, we need first to convert `ctypes` into the `appropriate abstract datatypes`. + +- `tensor`: Converts a **ctensor** into an **abstract tensor** + +- `to_int`: Converts `int32` - **machine integers** into the respective abstract datatype `int` + +- `ivector`: Converts an `array` into a `list`. There are neither arrays on the abstract side nor lists on the concrete level and that´s why such convertion is important. + + - Takes as inputs: the **pointer** to convert and the **desired size** of conversion + +For the other preconditions, what we do is a literal copy of the requirements already present at the abstract level but adapted to the concrete level. + +Translating the preconditions from the abstract level to the concrete level can be done in two different ways: + + 1. Translate `c entities` into the respective abstract ones and then literally express the same preconditions + + 2. Express that precondition, but resort to `concrete predicates` instead of the abstract structure. + +In the example above, the first precondition follows the `1.` pattern, while the second one follows the `2.`. There is not really a standard for this, as sometimes one is more readable and even provable than the other, so it is up to the specification author to choose which one to use. Having said so, the second **precondition** could also have been expressed through abstract terms: + +- `requires { size (tensor x) = size (tensor r) }` + +To verify that you have correctly expressed all these requirements you can call the respective **abstract function** and evaluate wether or not the verification conditions generated are automatically proved. + +This will help you undersand both if you have captured all the necessary requirements (among the ones already present) and what is the best way to express them. + +Check the following example: + +```why3 + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { r.t_rank = 2 } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let ghost _ = flatten (tensor x) (to_int axis) in + (...) +``` + +This a pure strategy for debugging the formal spec and therefore, it should not be included in the final version of the operator. + +The next images provide a better understanding of this strategy, depicting the verification conditions generated by the call to the abstract function and whether or not they are proved + +![Debugging VCs 0](./imgs/flatten_requires.png) + +If one had forgotten to include this precondition: + +``` +requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } +``` + +then, the strategy proposed above would have generated the following verification condition which is not automatically proved: + +![Debugging VCs 1](./imgs/flatten_requires_1.png) + +By inspecting the task menu, the specification author would be able to understand which precondition is missing and add it to the specification. + + + +**Ensures** + +Our final goal is to prove the **ensures** clause stating the refinement between both levels. + +Usually the following pipeline is followed to do so: + +$$\texttt{ensures} \xrightarrow{\text{proved by}} \texttt{assert} \xrightarrow{\text{proved by}} \texttt{invariants}$$ + +Whenever your invariants fail to prove the assertion, you can start by inspecting the invariants and they are likely to be wrong or at least lacking information relevant to achieve the proof. + +### 3.4 Invariants + +**Nested Loop Invariants** + +Operators such as Conv produce outputs whose number of dimensions is known at specification time. Their concrete implementations compute the output data through nested loops — one loop per output dimension. To prove refinement between the concrete and abstract functions — specifically, that the `data` field of the output tensor is correct — we must show that, for every valid coordinate traversed by the loops, the value written to the output tensor equals the value computed by the abstract specification at that same coordinate. Since these implementations rely on nested loops, the proof requires **loop invariants**. + +A loop invariant must satisfy two proof obligations: + +1. **Initialization** — the invariant holds for the first iteration. + +2. **Preservation** — if the continually holds for all iterations up to the last one, inclusive. + +When loops are nested, these obligations form a dependency chain: + +- an **outer** loop's invariant helps prove the **initialization** of the immediately inner loop's invariant + +- an **inner** loop's invariant helps prove the **preservation** of its enclosing outer loop's invariant. + +The following example illustrates this: + +```why3 +let ref sum = (f32 0.0) in +(* ... *) +for i = 0 to rows - 1 do + invariant { sum = 0.0 } + for j = 0 to cols - 1 do + invariant { sum = 0.0 } + for k = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) i j 0 k } + (* ... *) + done; + sum <- 0.0; + done; +done; +``` + +The innermost loop (`k`) accumulates a dot product in `sum`. + +For this accumulation to start correctly, `sum` must be `0.0` at the beginning of each `k`-loop — this is exactly what invariant $I_j$ states. In turn, proving the initialization of $I_j$ requires $I_i$ to guarantee that `sum = 0.0` when the `j`-loop begins. The initialization chain is therefore: + +$$I_i \xrightarrow{\text{initializes}} I_j \xrightarrow{\text{initializes}} I_k$$ + +In the opposite direction, once the `k`-loop terminates and `sum` is reset to `0.0`, $I_j$ is preserved, which in turn preserves $I_i$. + +### Loop Invariants for Proving Data Refinement + +**Example 1 - Unit Summation** + +To reason about this, please check the [summation](./examples/summation.mlw) example, which basically computes the unit summation over a given range. + +The invariant essentially captures the idea that until the current iteration `k`, the iterative summation has the same value as the recursive one. + +The image below depicts the verification conditions raised by such invariant as well as the reason why it does not prove: + +![Summation Invariant Preservation](./imgs/summation_invariant0.png) + +The goal and the respective logical context are below: + +![Summation Invariant Preservation](./imgs/summation_invariant1.png) + +What the solver is trying to tell us is that it does not yet have enough information to garantee that at the invariant proves to be valid after the current iteration. + +To solve this, we should somehow bridge the gap between the current iteration `i` and the next one `i + 1` also at the abstract level. + +Consider the following lemma, which states that the summation at iteration `i` is equal to the summation at iteration `i + 1` plus 1: + +```why3 + let rec lemma summation_lemma (i iter : int) + requires { i < iter } + ensures { 1 + summation (i + 1) iter = summation i iter } + variant { iter - i } + = + if i >= iter then + () + else + summation_lemma (i + 1) iter +``` + +After adding this, the invariant preservation is easily proved. + +This [file](./examples/summation2.mlw) already contains the lemma proposed above and the respective proof of the invariant preservation. + +**Example 2 - Dot Product** + +The example presented above is a very simple one, but the same strategy can be applied to much more complex operators. + +Let's have a look at a `dot_product` example, which is extremely useful to the `matmul` operator. + +The file is available [here](./examples/dot_product.mlw). + +Once again the proof is not achievable and the task menu displays the following: + +![Dot Product Invariant Preservation](./imgs/dot_product0.png) + +To solve this, we need a lemma that captures the relation above. Let us take into consideration the previous example lemma. + +The strategy is essentially the same: + +```why3 + let rec lemma dot_product_lemma (a b: tensor real) (row col i k iter: int) + requires { Int.(i <= k < iter) } + variant { Int.(k - i) } + ensures { + let a_val = a.data (Cons row (Cons k Nil)) in + let b_val = b.data (Cons k (Cons col Nil)) in + dot_product_rec a b row col i Int.(k + 1) = + Real.(dot_product_rec a b row col i k + a_val * b_val) + } += + if i < k then + Real.(dot_product_lemma a b row col (i + 1) k iter) + else + () +``` + +This lemma is enough to prove the invariant preservation, however, the why3 will fail to prove the invariant if we only add the lemma. + +![Dot Product Invariant Preservation](./imgs/dot_product1.png) + +To solve this, we need to explicitly call the lemma in the body of the loop, instantiating it with the appropriate variables. + +We will do so by introducing a ghost block: + +```why3 +ghost begin + dot_product_lemma (tensor a) (tensor b) (to_int row) (to_int col) (to_int i) (to_int iter) +end +``` + +After this, the invariant preservation is easily proved. + +More information regarding lemmas intantiation in [4.2](#42-ide-transformations-and-prover-hints). + +**Convolution** + +The Conv operator iterates over four output dimensions (`N`, `M`, `Y_H`, `Y_W`), producing four nested loops. + +Each loop carries an invariant stating that all output positions covered by completed iterations already hold the correct value — the same value that the abstract specification computes for those coordinates. + +Proving that the output data matches the abstract specification therefore requires four invariants, each satisfying initialization and preservation, linked together in the chain described above. + +The function `w_channels_calculate` computes the output value at coordinate `(nn, mm, yy_hh, yy_ww)`, corresponding to the current loop iteration. + +This concrete function is equivalent to the one used in the abstract specification of Conv, so the value it produces at any coordinate is exactly the value prescribed by the abstract specification at that same coordinate. + +The loop invariants ensure that this relationship holds for every coordinate traversed by the loops, thereby establishing data refinement. + +Among the four, the outermost invariant (for `nn`) is the most important: once the `nn`-loop terminates, it establishes the correspondence between the concrete and abstract computations for all coordinates in the output tensor. + +Below are the four loop invariants used in the concrete Conv implementation: + +```why3 + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; +``` + +The invariants alone are not sufficient for Why3 to discharge all proof obligations automatically. + +The core issue, which manifests at multiple levels of the loop nest, is always the same: when a value is written to `r.t_data[r_coords]`, the provers cannot automatically determine that previously written positions remain unaffected. + +They do not know that **distinct coordinates map to distinct flat indices**, and therefore that no position is ever overwritten. + +This must be asserted explicitly each time. + +### The innermost loop + +The first failure occurs on the **preservation** of the innermost invariant (the `y_ww` loop): + +```why3 + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } +``` + +The fix is to assert that, for every previously visited `yy_ww` coordinate, the coordinate vector differs from the one currently being written, the coordinate is within valid range, and the resulting flat offsets are distinct: + +```why3 + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; +``` + +With these assertions in place, the proof of the innermost invariant succeeds. + +### The outer loops + +The preservation of the outer invariants still fails. + +To understand why, consider the proof obligation that Why3 generates for the `y_hh` loop invariant: + +```why3 +constant yy_hh : int + +H3 : 0 <=' yy_hh + +H2 : yy_hh <' (y_hh +' 1) + +constant yy_ww : int + +H1 : 0 <=' yy_ww + +H : yy_ww <' int32'int y_w + +constant coords : list int = + Cons nn (Cons mm (Cons yy_hh (Cons yy_ww (Nil: list int)))) + +constant dims : list int = ivector (r.t_dims) (int32'int (r.t_rank)) + +------------------------------- Goal -------------------------------- + +goal c_conv'vc : + (value_at (r.t_data) @ offset1 coords dims).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm yy_hh yy_ww (int32'int str_h) + (int32'int str_w) (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left) +``` + +Inspecting the logical context more closely, we find the hypothesis corresponding to the `y_hh` loop invariant: + +``` +LoopInvariant2 : + forall yy_hh1:int. + 0 <=' yy_hh1 /\ yy_hh1 <' y_hh -> + (forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' int32'int y_w -> + (let coords'unused = + Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r1.t_data) + @ offset1 + (Cons nn (Cons mm (Cons yy_hh1 (Cons yy_ww1 (Nil: list int))))) + (ivector (r1.t_dims) (int32'int (r1.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 + (to_int2 (value_at (w.t_dims) @ 2)) (to_int2 (value_at (w.t_dims) @ 3)) + nn mm yy_hh1 yy_ww1 (int32'int str_h) (int32'int str_w) + (int32'int dil_h) (int32'int dil_w) (int32'int pad_top) + (int32'int pad_left))) +``` + +At first glance, the hypothesis and the goal look nearly identical. + +However, there is a subtle but critical difference: `LoopInvariant2` refers to `r1.t_data`, whereas the goal refers to `r.t_data`. + +The only relationship between `r` and `r1` available in the logical context is: + +```why3 +H13 : r = ctensor'mk (r1.t_rank) (r1.t_dims) (r.t_data) +``` + +Meanwhile, from the innermost loop invariant, the following hypothesis is also available: + +``` +LoopInvariant1 : + forall yy_ww1:int. + 0 <=' yy_ww1 /\ yy_ww1 <' (int32'int o +' 1) -> + (let coords'unused = + Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int)))) + in + (value_at (r.t_data) + @ offset1 (Cons nn (Cons mm (Cons y_hh (Cons yy_ww1 (Nil: list int))))) + (ivector (r.t_dims) (int32'int (r.t_rank)))).to_real = + w_channels_calculate (tensor x) (tensor w) + (to_int2 (value_at (w.t_dims) @ 1)) 0 (to_int2 (value_at (w.t_dims) @ 2)) + (to_int2 (value_at (w.t_dims) @ 3)) nn mm y_hh yy_ww1 (int32'int str_h) + (to_int2 str_w) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left)) +``` + +The underlying problem is the same as before: the provers know the values are correct, but they cannot establish that distinct coordinates yield distinct flat indices. + +This time, however, assertions cannot be placed directly at the `y_hh` loop level, because no writes to the output tensor's data occur within the `y_hh` loop body itself — all writes happen inside the inner `y_ww` loop. + +The solution is to **carry the `y_hh` loop invariant into the `y_ww` loop** as an additional invariant: + +``` +for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + +``` + +By placing the `y_hh` invariant inside the `y_ww` loop, the preservation of the original `y_hh` invariant is automatically discharged — as established earlier, an inner loop's invariant helps prove the preservation of its enclosing outer loop's invariant. + +However, Why3 still cannot prove the **preservation** of this newly carried invariant within the `y_ww` loop. + +The reason is the same: when we write to `r.t_data[r_coords]`, the provers cannot determine that coordinates from earlier `y_hh` iterations are not being overwritten. + +The same pattern of assertions resolves this: + +```why3 + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + +assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; +``` + +Applying this same strategy to the remaining outer loops (`mm` and `nn`) — carrying each outer invariant into the innermost loop and adding the corresponding non-aliasing assertions — completes the proof of preservation for all invariants, and thus establishes the data refinement of the Conv operator. + +The completed file is available [here](./examples/conv.mlw). + +# Part 4 - Tips, Hints and Strategies + +## 4.1. Scope Resolution + +When using modules that export functions with the same name (e.g., `+` from both `Int` and `Real`), it is probably the case that Why3 will automatically infer the wrong one and cause type errors. + +Take a look at the following example: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { iter - i } + = + if i < iter then + 1.0 + summation (i + 1) iter + else + 0.0 +end +``` + +If you attempt to open the Why3 ide with the above code, you will get the following error: + +![Type error due to scope resolution](./imgs/scope.png) + +This happens usually when a given function returns `a-datatype` whereas its auxiliar cauculus perform operations over elements with `b-datatype`. + +In that case, Why3 will infer the operations to be over `a-datatype` which is not what we want and will cause type errors. To solve this, we need to explicitly specify the scope of the operations by prefixing them with the module name. + +In the example above there are several operations whose operators are defined both for the `Int` and `Real` modules. + +Since we are returning a `Real` Why3 will try to perform every operation under the `Real` scope. + +To solve the issue we will have to specify the scope operations on the `Int` domain: + + +$$(iter - i) \rightarrow Int.(iter - i)$$ + +$$i < iter \rightarrow Int.(i < iter)$$ + +$$1.0 + summation (i + 1) iter \rightarrow 1.0 + summation Int.(i + 1) iter$$ + +The corrections applied to the code above are the following: + +```why3 +module OPSummation + use int.Int + use real.Real + + let rec function summation (i : int) (iter : int) : real + variant { Int.(iter - i) } + = + if Int.(i < iter) then + 1.0 + summation Int.(i + 1) iter + else + 0.0 +end +``` + +## 4.2. IDE Transformations and Prover Hints + +Although Why3 supports fully automated proving (by selecting proof levels 0–3), it is also possible to apply manual transformations and proof tactics to help the prover discharge difficult goals. + +Below are techniques we have found useful in practice. + +The full list of available transformations and tactics can be found under the **Tools** menu in the Why3 IDE. + +--- + +### Lemma / Axiom Instantiation + +When an axiom or lemma is stated universally, for example: + +```why3 +axiom test: + forall x: int. P(x) -> Q(x) +``` + +The automated prover may either fail or take significantly longer to apply it in a specific context. + +Manually instantiating the axiom with the concrete variables involved can make the proof faster and successful. + +**Example — Mean Value Theorem:** + +Consider the following axiom: + +```why3 +(* Lagrange Mean Value Theorem *) +axiom mean_value_theorem_sigmoid: + forall x y: real. + exists c: real. + (x <= c <= y \/ y <= c <= x) /\ + sigmoid_real x - sigmoid_real y = sigmoid_derivative c * (x - y) +``` + +And the following lemma to be proved: + +```why3 +lemma lipschitz_sigmoid: + forall x y: real. + abs (sigmoid_real x - sigmoid_real y) <= 0.25 * abs (x - y) +``` + +To prove `lipschitz_sigmoid`, the prover needs to apply the Mean Value Theorem. + +Instantiating the axiom with the specific variables `x` and `y` greatly helps. + +**IDE instantiation syntax:** + +``` +instantiate +``` + +If the axiom has more than one universally quantified variable, each variable must be instantiated in a separate step. + +The hypothesis produced by each instantiation is typically named `Hinst`, and subsequent instantiations should target it. + +This command is entered in the **"Type command here"** input field, located beneath the task list in the Why3 IDE. + +--- + +### Instantiation via Lemma Functions + +Instead of instantiating lemmas manually through the IDE, the same effect can be achieved by calling **lemma functions** directly in the body of the function being proved. + +This is the preferred approach, as it keeps the proof self-contained and reproducible. + +**Example — `valid_bounds_2` lemma function:** + +The following recursive lemma verifies that, given two `iarray` values `ks` (coordinates) and `ds` (dimensions), all coordinates within the range `[p, q[` are valid with respect to those dimensions: + +```why3 +let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end +``` + +Now consider the `dot_product` function, which allocates coordinate arrays at runtime: + +```why3 +let dot_product (a b: ctensor) (row col iter: int32) : (float, bool) = + requires { value_at a.t_dims 1 = value_at b.t_dims 0 = iter } + requires { a.t_rank = b.t_rank = 2 } + requires { iter >= 0 } + requires { row >= 0 /\ row < value_at a.t_dims 0 } + requires { col >= 0 /\ col < value_at b.t_dims 1 } + (* ... *) + let ref sum = (f32 0.0) in + let ref flag = False in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + if is_not_null a_coords_array && is_not_null b_coords_array then begin + flag <- True; + for i = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) row col 0 i } + set_ofs a_coords_array 0 row; + set_ofs a_coords_array 1 i; + set_ofs b_coords_array 0 i; + set_ofs b_coords_array 1 col; + + ghost valid_bounds_2 a_coords_array a.t_dims 0 2; + (* Rest of code ... *) +``` + +Although it follows directly from the preconditions that `a_coords_array` always holds valid coordinates for `a.t_dims`, Why3 does not infer this on its own. + +Calling `valid_bounds_2` as a ghost lemma function instantiates the lemma with the concrete arrays, giving the prover the exact fact it needs to continue. + +--- + +### Function Unfolding + +When a proof goal involves a function call, it can be helpful to force the expansion of that function so that the goal becomes more explicit and easier for the prover to handle. + +Two transformations are available for this purpose: `unfold` and `compute_in_goal`. Both are entered in the **"Type command here"** field, beneath the task list in the Why3 IDE. + +**`unfold`** expands a specific function by its definition at the point where it appears in the goal. + +To use `unfold` transformation, enter the following command in the **"Type command here"** field: +``` +unfold +``` + +Before applying `unfold`: + +![before unfold](./imgs/before_unfold.png) + +After applying `unfold`: + +![after unfold](./imgs/after_unfold.png) + +**`compute_in_goal`** performs a more aggressive expansion, reducing all computable expressions in the goal simultaneously. + +It requires no arguments: + +``` +compute_in_goal +``` + +Result after applying `compute_in_goal`: + +![compute_in_goal](./imgs/compute_in_goal.png) + + +--- + +## 4.3. How to Debug + +"Debugging" a formal proof means figuring out why the logical context is not sufficient to discharge a given goal. + +There are two common root causes: + +1. **The goal is false** — the specification, loop invariant, or postcondition is incorrect. The only fix is to correct what is being stated. + +2. **The goal is true, but the context lacks the necessary information** — the prover has the right goal but is missing a hypothesis, a lemma instantiation, or a rewrite step to connect the pieces. + +The most effective approach in either case is to carefully compare the **goal** with the **available hypotheses** in the logical context, looking for what bridges the gap. + +### Reading the Logical Context + +Consider the following goal: + +``` +------------------------------- Goal -------------------------------- + +goal w_cools_calculate'vc : + to_extended_real max_value = + w_cools_calculate (tensor x) (to_int2 h) (ww +' 1) 0 (to_int2 b) + (to_int2 c) (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) + (to_int2 pad_top) (to_int2 pad_left) (to_int2 pad_bottom) + (to_int2 pad_rigth) (to_int2 str_h) (to_int2 str_w) +``` + +In the logical context, among other hypotheses, we have: + +``` +constant x_val : float + +constant aux_flag : bool + +Ensures4 : + aux_flag = True -> + coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) + (to_int2 pad_left) = to_extended_real x_val + +constant ww1 : int321 + +constant ww : int = int32'int ww1 + +H31 : 0 <=' ww + +H30 : ww <=' int32'int o2 + +LoopInvariant : + to_extended_real max_value1 = + w_cools_calculate (tensor x) (to_int2 h) ww 0 (to_int2 b) (to_int2 c) + (to_int2 m) (to_int2 n) (to_int2 dil_h) (to_int2 dil_w) (to_int2 pad_top) + (to_int2 pad_left) (to_int2 pad_bottom) (to_int2 pad_rigth) (to_int2 str_h) + (to_int2 str_w) + +H1 : + w_cools_calculate (tensor x) (int32'int h) (int32'int ww1 +' 1) 0 + (int32'int b) (int32'int c) (int32'int m) (int32'int n) (int32'int dil_h) + (int32'int dil_w) (int32'int pad_top) (int32'int pad_left) + (int32'int pad_bottom) (int32'int pad_rigth) (int32'int str_h) + (int32'int str_w) = + max_extended_real + (coords_from_X_p (tensor x) + (Cons (int32'int b) + (Cons (int32'int c) (Cons x_h (Cons x_w (Nil: list int))))) + (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) + ...) + +Ensures : + to_extended_real (max max_value1 x_val) = + max_extended_real (to_extended_real max_value1) (to_extended_real x_val) + +constant max_value : float + +H : max_value = max max_value1 x_val +``` + +The logical context is sufficient to prove the goal. Here is the reasoning: + +**Left-hand side of the goal.** + +From `H`, we know `max_value = max max_value1 x_val`, so: + +``` +to_extended_real max_value = to_extended_real (max max_value1 x_val) +``` + +Applying `Ensures`: + +``` += max_extended_real (to_extended_real max_value1) (to_extended_real x_val) +``` + +**Right-hand side of the goal.** + +Since `ww = int32'int ww1` (constant definition), the call `w_cools_calculate ... (ww +' 1) ...` in the goal is identical to `w_cools_calculate ... (int32'int ww1 +' 1) ...`, which is exactly the left-hand side of `H1`. Applying `H1`: + +``` +w_cools_calculate ... (ww +' 1) ... + = max_extended_real + (coords_from_X_p (tensor x) (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) (int32'int pad_top) (int32'int pad_left)) + (w_cools_calculate (tensor x) (int32'int h) (int32'int ww1) 0 (int32'int b) ...) +``` + +From `LoopInvariant`, we know that `to_extended_real max_value1 = w_cools_calculate ... ww ...`, and since `ww = int32'int ww1`, the second argument of `max_extended_real` above equals `to_extended_real max_value1`. + +For the first argument, `Ensures4` tells us (given `aux_flag = True`) that: + +``` +coords_from_X_p (tensor x) (ivector x_p_coords_array 4) (to_int2 pad_top) (to_int2 pad_left) + = to_extended_real x_val +``` + +So it appears we have everything needed to close the goal — both sides reduce to `max_extended_real (to_extended_real max_value1) (to_extended_real x_val)` (up to commutativity). + +However, Why3 cannot discharge the goal automatically. + +**Why Why3 doesn't make the proof** + +The first argument of `max_extended_real` in `H1` is expressed using an explicit list constructor: + +``` +coords_from_X_p (tensor x) + (Cons (int32'int b) (Cons (int32'int c) (Cons x_h (Cons x_w Nil)))) + (int32'int pad_top) (int32'int pad_left) +``` + +whereas `Ensures4` refers to the same coordinate vector as `ivector x_p_coords_array 4`. + +These two representations are semantically identical, but Why3 treats them as syntactically distinct and cannot unify them on its own. + +The connection must be made explicit with an intermediate assertion: + +```why3 +assert { ivector x_p_coords_array 4 = + Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) }; +``` + +This assertion bridges the gap: once Why3 knows that `ivector x_p_coords_array 4` expands to that exact list, `Ensures4` can be matched against `H1` and the proof goes through. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png new file mode 100644 index 00000000..52618bc6 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/after_unfold.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png new file mode 100644 index 00000000..87d48f2a Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/before_unfold.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png new file mode 100644 index 00000000..2e90858d Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/compute_in_goal.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png new file mode 100644 index 00000000..76bd74e5 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product0.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png new file mode 100644 index 00000000..00948758 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/dot_product1.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png new file mode 100644 index 00000000..5e9ebcd0 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_not_proved.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png new file mode 100644 index 00000000..21e24a8b Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_VC_proved.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png new file mode 100644 index 00000000..13c1a7b1 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png new file mode 100644 index 00000000..594827c5 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/flatten_requires_1.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png new file mode 100644 index 00000000..6dd5f824 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/loop_inv.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png new file mode 100644 index 00000000..0c7dc871 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/refinement.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png new file mode 100644 index 00000000..fb027d8c Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/scope.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png new file mode 100644 index 00000000..b6f0fe73 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant0.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png new file mode 100644 index 00000000..21cbda15 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/summation_invariant1.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png new file mode 100644 index 00000000..5a120217 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/formal/imgs/termination.png differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/informal.md b/safety-related-profile/sonnx/ops/docs/guidelines/informal.md new file mode 100644 index 00000000..b4171eb3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/informal.md @@ -0,0 +1,330 @@ +# Introduction + +This document gives the guidelines to be followed when writing an operator's non-formal specification. Guidelines for the development of formal specification are given in a [dedicated document](./formal.md). + +# Rationales + +The non-formal specification is first an entry-point for any *user* of the SONNX profile. Towards that goal, it must explain clearly the purpose of the operator, i.e., the relation between its inputs (arguments and attributes) and its outputs. Towards that goal, it may use whatever means deemed useful including text description, mathematical formulae, graphics, examples. + +It must also provide a clear statement about: +- the usage restrictions for the operator imposed by SONNX, +- the constraints on the arguments and attributes of the operator (or pre-conditions") that must be satisfied for the operator to be applicable, +- the possible errors that may raise during the execution of the operator. + +In the following paragraphs, we express some of the "principles" that we applied to eleborate the guidelines. + +#### About the level of formalization +The non-formal specifications aims at providing a clear and complete description of the behavior of the subset of operators considered in the SONNX profile. Understandability is favored over formalization, accordingly, the non-formal specification essentially relies on natural language, with mathematical formulae when deemed necessary. In addition, for each operator, a formal specification expressed using a formal language (Why3) is provided separately. + +#### About data types +The non-formal specification is given for all data types considered in SONNX i.e.: int8, uint8, int16, uint16, int32, uint32, int64, uint64, float, double and float64. In addition, SONNX also provides a specification of each (mathematical) operator for real numbers. This is aimed at facilitating the understanding of the core function of the operator without dealing with implementation-specific issues such as the effect of overflows (for ints and uints) or the handling of special float values (+Inf, -Inf, NaN, 0+, 0-). + +#### About floating point numbers + +Floating point operations are specified in compliance with the the IEEE 754 standards (IEEE Standard for Floating-Point Arithmetic, IEEE Std 754 2019, IEEE). In particular, care is taken to describe the expected behaviour of mathematical operator when dealing with IEEE special values (+Inf, -Inf, +0+, -0, and NaN). + +#### About the compliance with implementation standards + +SONNX is essentially a specification effort. As such, it must remain independent from any specific implementation and, instead, must simply express the intended behavior of each operator according to some usual and/or consensual understanding of its semantics. However, tests realized by the SONNX WG on the implementations of some operators (e.g., **MaxPool**) show that the actual behavior sometimes differs significantly from the expected one, due to implementation choices or, sometimes, implementation bugs. + +Even if we consider that the SONNX specification shall not "reflect" a specific implementation, it shall preferably stick to the ONNX Runtime implementation when choices have to be made. When, for a given operator, the usual/consensual semantics and the ONNRtime implementation diverge, the SONNX WG choose to indicate this discrepancy. This is for instance the case for the **MaxPool** operator. +Note also that the semantics of an operator in ONNX runtime may depend on the execution backend (CPU, GPU,...). + +#### About specifying and non specifying information and traceability + +By default, all the text of the specification is "specifying" (i.e., it expresses a constraint to be followed). + +Reference to the specification elements can be done using the section names (tagged `#` in the markdown document). + +If a more precise location must be identified, a tag can be used. tags can be nested. +[spec1] +This is some traceable text. +[spec1.1] This is another text. [/spec1.1] [spec1] + +Additional *non specifying* information may may be given. In that case, it must be enclosed between the following tags: + +`[info]
` + +`[/info]
` + +For instance: +
[info]
+This is non specifying information. +
[/info]
+ +#### About error conditions + +An error condition is a condition leading to an unexpected result. Obviously, if the specification is clear and complete, all possible behaviour should be "expected". However, we consider worth being mentioned any result leading to +- an "unexpected result", such as a division by zero in the integer domain +- a NaN in the floating point domain *when this NaN is not simply the propagation of a NaN in the operator inputs* + +In some cases, a pre-condition may be stated on the inputs so that, if the pre-condition is satisfied, then no error -- NaN, division by zero, etc. -- can occur. For instance, no division by zero can occur if the condition stating that "the denominator shall not be null" is satisfied. Nevertheless, the possibility for an error to occur will still be signaled to the reader. + +Systematically providing pre-conditions on the inputs to prevent error conditions was not considered useful for the end-user since operators are generally used in complex graphs for which verifying the preconditions to prevent the occurrence of error is not achievable in practice. + +By default, pre-conditions are supposed to be satisfied for an operator to be defined or, stated the other way round, if at least one precondition is not satisfied, then the behaviour of the operator is undefined. In some cases, a pre-condition can be violated but the behaviour is nevertheless defined. For instance, when applying **MapPool** with no padding, the pooling kernel can be larger than the argument tensor, which violates one precondition. In that case, SONNX states that the operator shall return an empty tensor. This specific behavior must be specified in the error section. + +#### About accuracy + +The SONNX profile does not specify the accuracy of operators, instead, it provides guidance on how to analyze the propagation and introduction of errors on a given implementation and illustrates the approach on SONNX' reference implementation. + +The analysis is carried out in two steps: +- During step 1, an analysis is carried out to specify bounds on propagated and introduced errors considering the IEEE rounding error. This bounds may not be the tightest one, but a bound that can be determined with a reasonable analysis effort. +- During step 2, an analysis of the code of an actual implementation (in our case, the reference implementation) is carried out using a tool developed for that purpose by one of the SONNX WG contributor (Franck Vedrine, [CEA List](www.cea.fr/)). + +Please refer to the [specific set of guidelines](../../docs/guidelines/accuracy.md) are given for the analysis of accuracy. + +# Specification guidelines +This section is composed of two sub-sections: +- *General guidelines* defining presentation rules such as fonts, notations, use of tags for tracability, etc. +- *Structure and Contents of the specification* defining the organization and the contents of the non-formal specification of an operator. This section applies the general guidelines. + +## General guidelines +The non-formal specification is intended for both users *and* implementers of operators who both need to understand what an operator does and how to use it. For instance, the first kind of readers might be satisfied with one or two sentences about the semantics of an operator whereas the second category of readers would like to get all the details of the semantics. + +More precisely, the non-formal specification: +- Is aimed at showing clearly what a given operator is supposed to do, +- Without calling on a strict formal, mathematical language, +- Knowing that the exact and complete specification is given in the "formal" specification. +- May provide diagrams and examples to make things clear. +- Follows ONNX nomenclature, which includes naming convention, for operator names, types, identifiers of operator inputs, outputs and attribute, etc. + - Examples: + - The element-wise addition of tensors is $Add$, not $add$ + - The 16-bit floating-point type is $float16$, not $FP16$ + +The writer of the non-formal specification must take care to keep it readable and understandable by a ML developer. The recommendations given in the following guidelines target this objective. + +### Styling +- Mathematical objects are represented using *italic*. LaTeX formulae are used. +- In the text, operator attributes are represented using `this font`. +- As far as possible, names of arguments and attributes shall be used in mathematical formulae. In the case the name is "too long", another, shorter designation, may be used with a clear statement of the redefinition. If the symbol refers to a greek symbol (e.g., $\text{alpha}$), the symbol itself can be used (e.g., $\alpha$). + +### Basic operations +The specification may use the following operations without first defining them: +- $+$, $-$, $*$, $/$, $-x$ (negation), +- $\sin(x)$, $\cos(x)$, $\tan(x)$, +- $\exp(x)$, $\sqrt x$, $\ln(x)$, $|x|$ +- $\min(x,y)$, $\max(x,y)$ +- logical operators: $\wedge$, $\vee$, $\lnot$ + +### Naming conventions +- As far as possible, ONNX names for inputs, outputs, and attributes must be used. + +### Notations +#### Tensors +- A tensor is always represented in uppercase letters (e.g., $A, B,...,X, Y, Z$). +- In the case of a variadic operator (e.g., **Concat**), the tensor parameters are designated by an index: $A0$, $A1$, etc. Indexes start at 0 to be consistent with the other use of indexes. +- The rank of a tensor $T$, i.e., the number of its dimensions, is denoted $rT$. +- The shape of a tensor $A$ is denoted by a vector $(dA_0, ..., dA_i, ..., dA_n)$ where $dA_i$ refers to the dimension along axis $i$ and $n=rA-1$. +- For a tensor used as a variadic parameter (denoted $Ai$), the shape is denoted by $(dAi_{0}, dAi_{1}, ...)$. +- A specific element of a tensor is denoted: + - either as: $A[i]$, where $i$ is a [tensor index](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md#tensor_index) + - or as: $A[i_0, i_1, ..., i_{rA-1}]$. + +#### Tags +The non-formal specification makes use of three different types of tags: +- A **restrictions tag** expresses a restriction with respect to the ONNX standard (see the section about restriction below). They are indicated by tag `[R]` where `` is a number.\ +A synthesis of all restrictions is given in section "Restrictions" (see below). +- A **constraints tag** expresses a constraint on one or several inputs, outputs, or attributes. They are indicated using `[C]` where `` is a number. +- A **traceability tag** identifies a specific location in the non-formal specification. These tags are used to establish traceability between the informal and formal specifications. They are indicated by `[T]` where `` is a number. + +For instance, here is a tag introducing a constraint relating some input and output tensors: +>`[C1]` Shape consistency \ +> Statement: Tensor $A$ and $Y$ shall have the same shape. + +When part of the documentation refers to a tag, an hyperlink is used. This is achieved using the following elements: +``` + `[T1]` +``` + to declare the location of the tagged element and + ``` + [`[T1]`](#my_tag_name). +``` +to refer to the tagged element. + +Here is an example:
+`[T1]` This is a tagged paragraph. + +This is a reference to the tagged paragraph [`[T1]`](#my_tag_name). + +### Types +- The type names shall be the ones used in the ONNX description of the operators, without surrounding them with "tensor()". + - Example: "tensor(double)" in ONNX becomes "double" in the non-formal specification. +- The data types allowed in SONNX operators are: + - IEEE 754 floating-point types: double, float, float16 + - Signed integer types: int64, int32, int16, int8 + - Unsigned integer types: uint64, uint32, uint16, uint8 + - bool + - string +- IEEE 754 floating-point types, i.e., double, float and float16 have the following special numbers: + - +0 and -0 + - +inf and -inf + - NaN (Not a Number) +- All operators applicable to numeric values shall first be specified for values in the domain of real numbers, then specific descriptions shall be given for the other types (float, double, etc.). +- A description can be applicable to multiple types as long as its **semantics description** remains the same for all types. + +## Structure and contents of the specification + +This section describes the required structure and contents of the non-formal specification of an operator. + +The [specification template](informal_spec_template.md) gives an example of the required structure. + +### Contents + +This section gives the list of all non-formal specifications of the operator, for each of the applicable types, with hyperlinks to the sections (see [template](informal_spec_template.md)). + +- **Op** operator for type real +- **Op** operator for types <T1>, <T2>,... +- **Op** operator for types <T1>, <T2>,... +- etc. + +The reference, with link, to the ONNX definition of **Op** shall be inserted in this section. See **Div** example below. + +Here is an example for operator **Div**: +> Contents +>- **Div** operator for type real +>- **Div** operator for types float16, float32, float64 +>- **Div** operator for types int8, int16, int32, int64, uint8, uint16, uint32, uint64 +> +> Based on ONNX [Div version 14](https://onnx.ai/onnx/operators/onnx__Div.html). + +The following section must be repeated for each set of types for which the semantics is the same. One section corresponds to one entry in the "Contents" list. + +## **Op** (, ,...) + +### Signature + +Definition of operator **Op** signature: + + $O = \textbf{Op}(X,Y,...,Z)$ + + where + - $X$: Brief description of argument $X$ + - $Y$: Brief description of argument $Y$ + - ... + - $O$: Brief description of output + +Arguments shall have different names. For instance + +$O = \textbf{Op}(X, Y)$ + +When the same name is used for different arguments such as in + + $O = \textbf{Op}(X0,X1,...,XL)$ + + this means that the operator is **variadic**, i.e., it accepts a variable number of arguments. In this example, there are n arguments that are discriminated by their index. + +### Restrictions +This section lists all restrictions applicable to the operator. A restriction is **a limit with respect to the normal usage domain** of the ONNX operator. A restriction may concern the dimension of tensors, the values of attributes, etc. + +Some SONNX restrictions apply to all the operators. Therefore, this section shall contain the following markdown link: + +`\[General restrictions](../common/general_restrictions.md)` + +Restrictions marked as "Transient" are introduced by the working group in order to reduce the specification, proof, etc. effort. Those restrictions, which are not traceable to a need, are aimed at being eventually relaxed. However, in the meantime, both transient and non-transient restrictions are applicable by the operator user or implementer. + +Restrictions not marked as "transient" are traced to some [end-user requirement](../../../../deliverables/reqs/reqs.md) using an hyperlink. + +An example is given hereafter + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `[R1]` | Input tensor $X$ has 2 spatial axes | Transient | +| `[R2]` | Attribute `auto_pad` is restricted to NOTSET | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | + + ### Specification + + This section contains the specification of the operator. The specification is "informal", ie., it does not use a formal language, even though it usually uses some mathematical formulae. The specification shall be readable, understandable, and self-contained. It can include illustrations if deemed necessary. The objective is that a human being can fully understand the domain, range, and semantic of the operator with no additional information. Stated differently, he/she should be able to implement the operator with no additional information. + +The specification shall be composed of the following parts: +- A short description of the operator. For instance, for the $Conv$ operator: + +> Operator **Conv}** computes the convolution of the input tensor $A$ with the kernel $W$ and adds bias $B$ to the result. Two types of convolutions are supported: standard convolution and depthwise convolution. + +- A detailed description that: + - Uses the notations proposed in section "Notations" of these guidelines + - Implements the traceability tags proposed in Section "Tags" of these guidelines + - Presents the mathematical formulae, if necessary, according to the following pattern: the complete formula is first given and its atomic elements and sub-expressions are defined afterward, by introducing them with "Where" or "In which". + - Insert a blank line before and after each formula so that it renders correctly in the browser. + +For instance, for the **Conv** operator: + +> $$ + Y[b, c, m, n] = \sum_{i=0}^{dW_1-1} \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1} \\ (X_p[b,i,m \cdot \text{strides}[0]+ j , n \cdot \text{strides}[1]+ z ] \cdot W_d[c, i, j, z]) \\ + B_b[c] +$$ +> Where +>- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$ +>- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$ +>- $m \in [0,dY_2-1]$ is the index of the first spatial axis of output $Y$ +>- $n \in [0,dY_3-1]$ is the index of the second spatial axis of output $Y$ +>- etc. + +## Examples + +The specification must provide examples to illustrate the behaviour of the operator. As far as possible, examples must cover special values, domain bounds, etc., in order to clarify the behaviour of the operator, especially for non trivial cases. If possible, a jupyter notebook allowing the generation of the examples shall be provided (and placed aside the specification document). + +When the displayed result is not exact (for instance when using some real or float numbers that cannot be represented exactly), please use the $\approx$ symbol instead of the $=$ symbol. + +## Error conditions + +This section identifies the conditions that may occur during the execution of the operator leading to unexpected results (e.g., a IEEE special value while there was no special value in the inputs, a runtime error, etc.) + +When writing a specification, the writer must identify the following failure conditions: +- for floating point computations + - invalid operation as defined in IEEE 754 section 7.2, i.e. + - $0 \times\infty$ or $\infty \times 0$ + - addition or subtraction or fusedMultiplyAdd: magnitude subtraction of infinities, such as addition $(+\infty, -\infty)$ + - division $(0, 0)$ or division ($\infty, \infty)$ + - remainder(x, y), when y is zero or x is infinite and neither is a NaN + - square root if the operand is less than zero + - etc. Refer to the standard for a complete list of error conditions. + - overflows (computations leading to +Inf or -Inf) +- for integer computations + - division by zero, + - overflows, i.e., operations leading to a value out of the range (e.g., addition of two large int32 values non representable in int32) + +The following rules must be applied. + - Restrict the input domain to prevent the occurrence failures if it is not too conservative. for instance, $x \ge 0$ for $sqrt(x)$ is deemed acceptable whereas, using `uint32`, $A\leq2^{31}$ and $B\leq2^{31}$ for $A+B$ is deemed too conservative. + - Give the most detailed description of the conditions in which a failure can occur, and the possible expected result. For instance for a matrix multiplication in int32, explain that the accumulator may overflow and may wrap around, leading to an incorrect and inconsistent result. If possible, point out the location in the specification where the error may occur. +- If no indication is given about occurrence of a "failure", this means that the operator returns a correct value (as per specification) for **any** input value in the domain defined by the type. +- If applicable and possible, provide "recommendations" about the implementation to prevent failure. For instance, propose to substract $max(Xi)$ to the argument to make the **Softmax** operator more robust. + +Note that these rules concern the *specification* of the operation. Therefore, they must be independent from implementation choices. For instance, *generally speaking*, it is always possible for the operation to overflow if the domain is output domain limited (e.g., `int32`), so there must be a warning about this failure condition. Nevertheless, a specific implementation may be failure-free if, for example, the size for the matrices is limited and the accumulator is sufficiently large. In that case, the implementation must give these conditions. Otherwise, the implementation is deemed compliant with the specification. + +## Attributes +This section describes the operator's attributes. This section must be introduced in the section about real numbers. Sections concerning the other types (floats, integers) shall shall not repeat this contents and provide a link to it whenever applicable. See the [template](./informal_spec_template.md) for an example. + +### `name`: \ +where `name` is the attribute's name and \ is the attribute's type. + + #### Constraints +This section gives all constraints applicable to the attribute. + +- When a constraint involves several inputs/outputs/attributes, it is only described once when the first input or attribute concerned by the constraint is described. Then, for the other inputs, attributes, or outputs concerned by the same constraint, a cross-reference is given. +The description is structured as follows:* + + - `[C]` <Title of constraint> + - Statement: <Expression of the constraint> or cross-reference to the previous location where this constraint was first introduced. + - Rationale: <Justification for the constraint>. When the title and/or the statement of the constraint are sufficiently explicit, the rationale may be omitted. + +## Inputs + +This section describes the operator's inputs. + +### $\text{name}$: \ + +where $\text{name}$ is the name of the input and \ is the type of the input. + +#### Constraints +Same as for the attributes. + +### Outputs + This section describes the operator output. + +### $\text{name}$: \ +where $\text{name}$ is the output's name and \ is the output's type. + + #### Constraints +Same as for the inputs. + + End of the document. diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md b/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md new file mode 100644 index 00000000..9fc5d8c6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/informal_spec_template.md @@ -0,0 +1,100 @@ +*This template only covers one datatype. Do not forget to describe the operator for real numbers and all applicable data types. For structural operators (i.e., operators whose behaviour do not depend on the data type), a single section may suffice.* + +*The text in italics gives indications. It shall not be retained the specification document.* + +# Contents + +- **Op** operator for type [int8](#int8) + + +Based on ONNX [Op version 14](https:/...). + + +# **Op** (int8, int8) + +## Signature + +$Y = \textbf{Op}(A, B)$ + +where: +- $A$: *brief description of the first argument* +- $B$: *brief description of the second argument* +- $Y$: *brief description of the output* + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **Op** operator: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | *Brief decsription of the restriction* | Transient | +| `[R2]` | *Brief decsription of the restriction* | [Link to the restriction in the req document](../../../deliverables/reqs/reqs.md#restriction) + +*Note: If the restrictions apply to all types, they can be described only once and be replaced by the following text: + +`See [Restrictions](#restrictions).` + +*See for instance the case of operator [**MaxPool**](../../spec/informal/maxpool/maxpool.md)*. + +## Informal specification + +Operator **Op** [...] *description of the purpose of the operator* [...] + + +The effect of the operator is illustrated on the following examples. + +### Example 1 +*Example of usage of operator **Op**".* + + +## Error conditions +*Description of "error conditions", i.e.,cases where the value produced may be unexpected.* + +## Attributes + +### `ATTR`: int8 + +*Attribute description.* + +*Note: if the attribute does not depend on the type of the argument (real, float, etc.), then it can be described only once (e.g., in the section for real numbers).* +*See for instance the case of operator [**MaxPool**](../../spec/informal/maxpool/maxpool.md)*. + +#### Constraints + + - `[C1]` [See constraint (C1) on A](#C1ia). First constraint on $ATTR$ + - Statement: *Description of the first constraint on `ATTR`. + + +## Inputs + +### $\text{A}$: int8 + +*Description of the first input.* + +#### Constraints + + - `[C1]` First constraint on $A$ + - Statement: Description of the first constraint on $A$ + +### $\text{B}$: int8 + +*Description of the second input.* + +#### Constraints + + - `[C1]` First constraint on $B$ + - Statement: Description of the first constraint on $B$ + +## Outputs + +### $\text{Y}$: int8 + +*Description of the output.* + +#### Constraints + + - `[C1]` First constraint on $Y$ + - Statement: Description of the first constraint on $Y$ + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md b/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md new file mode 100644 index 00000000..d05ccc11 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/lifecycle.md @@ -0,0 +1,22 @@ +The lifecycle of the informal specification, formal specification, and tests item is managed using Github's [SONNX project](https://github.com/users/ericjenn/projects/4) + +- I you don't have write access, please ask Eric. + +- If you start working on a new operator, create an item in the "Dev in progress" column. + - Use the following conventions (replace "informal" by "formal" or "test" depending on the item). + - Use + - Title: Informal space "" + - Description: Develop informal specification of operator [](https://onnx.ai/onnx/operators/onnx__.html) + - Assign it to you + - Set the label to "Informal", "Formal" or "Test" in order to facilitate filtering. +- Then, move the item to the appropriate column depending on its state.: + - Dev in progress: (the spec / code / ...) is being developed + - Ready for review: the item has been created and is ready to be reviewed + - Review in progress: the item is being reviewed + - Review complete: the item has been reviewed, it is ready for corrections + - Correction in progress: the item is being corrected. Once corrected, it moves either to "Completed" if no new review is necessary or to "Ready for review" if a new review is necessary. + - Corrections completed: the item has been corrected according to the review. Reviewers can chek that the corrections have been correctly taken into account. Once OK, the item moves to "completed". + - Completed + +- Items in the kanban are reviewed during the SONNX meetings. +- Use the Github's "Comment" to make your comments (preferably), or create a file ".md" in a "Reviews" folder of the directory corresponding to the operator (see [here](https://github.com/ericjenn/working-groups/tree/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/add) for the add operator). diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md new file mode 100644 index 00000000..e03f0178 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-em.md @@ -0,0 +1,477 @@ +**EM: my notes and correction are in bold** + +## Numerical Accuracy Specification + +### Introduction + +#### Objectives and limits + +This document provides theory and techniques to write a formal specification for the +accuracy of numerical components used in neural networks. The result is +a specification that should be verified by any implementation, **under a narrow set of acceptable assumption. Here is a list of accepted assumptions:** + +* floating-point computation **conforming** to the IEEE-754 standard +* fixed-point computation **[EM: for a given bit width? AFAIK, the ONNX standard only offers integer types, ergo any treatment of integer vs fractional part is left implicit]** +* concrete or symbolic range for the input values **[EM: do we restict the language in which symbolic ranges/constraints can be expressed? Any first-order logical expression over the reals?]** +* symbolic constraints over some computations + +There **may** exists **different** specifications for the accuracy **of the same operator**. For instance, the specification for the matrix multiplication will be different if one matrix is diagonal or if both matrices are dense with a same order of magnitude for every coefficients. + +We will favour short formulas, even if they may seem approximate. **[EM: are they approximate or not?]** + +#### Numerical Error + +This section provides a tight and verifiable specification of the numerical error +on the operator's results. **In general, we define the numerical error as** the difference between the current implementation on data with error resulting from previous approximated computations and an ideal algorithm operating on data also coming from previous ideal computations. **I.e. we consider the problem of error accumulation throughout the SONNX graph, rather than studying each operator in isolation** + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +This framework decomposes the error into two parts: + +* the first, the propagated error, depends on the numerical + error and the numerical values of the inputs - in particular, it is independent of the + implementation and the storage format +* the second part, the introduced error, depends on the + concrete value of the inputs and the implementation with its storage format. + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$ + +The error associated with the result of the operator corresponds to the sum of the propagated errors and the introduced error. **This new error is then propagated to** the next operator. + +#### Specification and Verification Strategy + +The provided specification **is based on** an over-approximated semantics of the +numerical error of native computer operations approximating real number +operations **(e.g. IEEE-754).** In order to preserve the readability of the formulas, the general specification introduces additional (conservative) simplifications compared to the original specifications. +However, this general specification may be too over-approximated for some specific inputs (**e.g.** tensor representing diagonal matrices). In this case, more precise specific specifications are provided alongside the general specification. + +The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries. + +### Error Propagation + +**We split our analysis of numerical error in two parts. First, we analyse the propagation of input error through ideal operators. Then, we analyse the additional error introduced by non-ideal implementations. + +#### Ideal Operators + +**[EM: this section requires considerable polishing. I leave my suggestions as notes]** + +This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator. + +It is only for information, since the properties does not depend on the implementation. The formula aims to explain how an input error is amplified by the operator just from its functional description. **[EM: why is it "only for information"? It seems quite important to me: it's one of the two error terms we must compute!]** + +**[EM: to make the point more general, why not $f: \mathbb{R}^n \longrightarrow \mathbb{R}^m$?]** +From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and +an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$.**[EM: I am not sure this notation is useful. Why not keeping the $x^i+e^i$ notation used above?]** + +The **propagated error** of the $f$ function as ideal operator is + +$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$ + +**[EM: with the simpler notation, this becomes:]** +$$f(x^0 + e^0, \ldots, x^{n-1} + e^{n-1}) - f(x^0, \ldots, x^{n-1})$$ +**[EM: which would still require the reader to understand that $op_{ideal}$ above has become $f$ here. I would try to minimise the cognitive effort the reader needs to put.]** + +Hence if $f$ is derivable two times, the formula**[EM: something is missing here; we cannot differentiate w.r.t. $\delta x^i$ since it is defined as a vector $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$. Furthermore, the definition of $X_{err}\leq 1$ is completely lost on me.]** + +$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) + \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$ + +**[EM: I assume we are building a first-order Taylor expansion here w.r.t. the error term. In my simplified notation, it becomes:]** +$$e_{prop}=\sum_{0 \leq i < n}\frac{\delta }{\delta e^i}f(x^0 + e^0, \ldots, x^{n-1} + e^{n-1})e^i+\mathcal{O}(e_i^2)$$ +**[EM: but it still contains some mysterious terms, e.g. why are we summing over $i$? Shouldn't we compute the full Jacobian matrix, since $f: \mathbb{R}^n \longrightarrow \mathbb{R}^m$? Even for a scalar function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^1$, the Taylor expansion would produce a gradient vector; why not taking its norm, rather than adding the terms (which might cancel out)?]** + +is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$: + +$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$ + +If there are no error on the arguments, the propagated error is $0$. +Otherwise, we **recommend** a first order expression like: **[EM: I vaguely understand the intuition behind taking the L1 norm (absolute value); however, a clearer explanation should be given to the reader (e.g. in terms of soundness of the overapproximation). Also, are we still computing a separate error term for each function output?]** + +The absolute value of the propagated error is less or equal than + +$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$ + +##### Example 1 (ideal multiplication) + +For the multiplication $f(x, y) = x\times y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$, the **propagated error** $PE(f)$ is + +$$\begin{array}{rcl} +PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\ + & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}} +\end{array}$$ + +that is simplified into + +$$\begin{array}{rcl} +PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$. + +Hence, the accuracy definition of the operator will just indicate that + +$$\begin{array}{rcl} +|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional, since it is always **[EM: not sure what "optional" means here. That we do not need to estimate the higher-order term because it is small? We might still need to do so if we want a sound over-approximation...]** + +$$\begin{array}{rcl} + \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\ + & = & (x_{\textit{val}} + x_{\textit{err}})\times(y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} - (y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}})\\ + & = & x_{\textit{err}}\times y_{\textit{err}} +\end{array}$$ + +##### Example 2 (ideal division) + +For the division $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$, the **propagated error** $PE(g)$ is + +$$\begin{array}{rcl} +PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +that is simplified into **[EM: by using the first-order Taylor expansion?]** + +$$\begin{array}{rcl} +PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional **[EM: ditto]** + +$$\begin{array}{rcl} + \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\ + & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +Hence, the accuracy definition of the operator will just indicate that + +$$\begin{array}{rcl} +|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +##### Example 3 (ideal matrix multiplication) + +Tensor operators require a **more complex** methodology, since the algorithm combine many atomic operations on $\mathbb{R}$. **Again, the objective is deriving a sound** over-approximation of the propagated error. + +**[EM: we suddenly switch to code, rather than standard mathematical notation as above. This is not good, since we haven't yet introduced how the code analysis work. In my opinion, examples should always be introduce after the theory.]** + +1. Naïve Algorithm Description + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop **[EM: there is an assumption that the error terms are in the form $|a[i][k]|\leq ae$, rather than element-wise $|a[i][k]|\leq ae[i][j]$. This follows the spirit of simplification stated at the beginning of the document. However, it is a big design choice which (in my experience) will introduce a great deal of over-approximation; thus, it should be given the space it deserves. E.g. create a list of requirements/recommendation and clearly state: "For each variable/tensor $x$ in the SONNX graph, we will compute a single error value $x_e$, such that all variable/tensor entries satisfy $|x[i,j,\ldots,k]|\leq x_e$".]** + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // OAPE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + // |PE(C[i][j])| <= a*be + b*ae + C[i][j] += A[i][1]*B[1][j]; + // |PE(C[i][j])| <= 2*a*be + 2*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant **[EM: the reader must be well-versed in software verification and static analysis to know what a loop invariant is]** + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // |PE(C[i][j]| = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j]| <= k*a*be + k*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verify a loop induction, it becomes a loop invariant **[EM: how do we prove it satisfies a loop induction? We do not show it here]** + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j])| <= k*a*be + k*b*ae + + int k = 2; + // if |PE(C[i][j])| <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop **[EM: what is the post condition? We have not specified it]** + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |OAPE(C[i][j]| <= k*a*be + k*b*ae + // |PE(C[i][j])| <= p*a*be + p*b*ae + ``` + +3. Final specification **[EM: shouldn't this be presented at the beginning of Example 3? That way the reader would have an idea of what the objective is.]** + + At the end, we can specify that + + * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$, + * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$, + * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$, + * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$ + +#### Non-Ideal Operators + +This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$, +the introduced error, where $Y$ is the tensor result of an operator. +The objective is to provide a specification **over the actual implementation of an operator.** Hence + +$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +The introduced error is an over-approximation of difference between the implementation result and the ideal result when there is no error on the input. + +**[EM: note that we switched back to the notation $op$ instead of $f$. Also, in the document introduction, the implementation error is computed over $\overrightarrow{x+e}$ rather than $\overrightarrow{x}$. The next paragraph reads like an attempt at explaining whether this change of variables makes a difference, but it is not clear enough.]** + +From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation. It is always defined as the difference between the implementation result and the ideal result, for which we additionaly substract the propagated error of the previous section. + +##### IEEE-754 implementations + +**[EM: what about fixed-point/integer/quantised ones? Will we add them to the guidelines later?] + +For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator +whose result $r$ is a normal floating-point number, the introduced error is less or equal than +$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode +round to nearest even. **[EM: this is an oversimplification, as it does not take into account subnormal numbers and catastrophic cancellation. If the result of the operation is $r\approx0$, the actual error might be much larger than $|r|\times\frac{\varepsilon}{2}$]** Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval +may contain denormal numbers), the introduced error is less or equal than +$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even. +There exists more accurate formulas, but such formulas usually take too much +details into account.**[EM:I approve of our simplification objective, but is our error estimate sound? If not, I feel we should give the readers more details on our design decisions, to convince them that what we are doing is sensible.]** + +###### Example 4 (IEEE-754 multiplication) + +For the multiplication $f(x, y) = x * y$, the **introduced error** $IE(f)$ should be less or equal than **[EM: are we using "should" as a prescriptive word here (e.g. any SONNX-compliant implementation must satisfy this)? It is the first truly prescriptive statement in this document.]** + +$$\begin{array}{rcl} +|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided $|x|\times |y|$ is a normal number **[EM: should we give some extra context in these examples. E.g. "the IEEE-754 standard mandates that any implementation of the multiplication operator $x_{float}\times_{float} y_{float}$ must yield the same result as executing the operations in infinite-precision (ideal) arithmetic and then rounding the result, i.e. $float(x_{float}\times y_{float})$. Hence, the error only depends from the magnitude of the result $|x|\times|y|$ and the representable precision $|u|$."]** + +$$\begin{array}{rcl} +|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + +$$\begin{array}{rcl} +|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + + +$$\begin{array}{rcl} +|IE(f)| & < & 1 +\end{array}$$ + +for any fixpoint implementation. **[EM: we only mentioned fixed-point implementation en passant so far. This error definition makes me think we are referring to integer implementations, otherwise the error would be fractional. Also, how does ONNX/SONNX deals with integer overflows?]** + +###### Example 5 (IEEE-754 division) + +For the division $g(x, y) = x / y$, the **introduced error** $IE(g)$ should be less or equal than + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$\frac{|x|}{|y|}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & 1 +\end{array}$$ + +for any fixpoint implementation. **[EM: see the above comments about fixed-point for multiplication (no overflow here).]** + +###### Example 6 (IEEE-754 2D matrix multiplication) + +Tensor operators require a methodology to find an **over-approximation of the introduced error**, since their algorithms combine many atomic operations. **[EM: many of my comments on Example 3 are also valid here]** + +1. Naïve Algorithm Description + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop **[EM: I do not understand where the term $2*a*b*(1+u)*u$ comes from. Is it a result of the accumulation/addition? More explanation is needed.]** + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + // |IE(C[i][j])| <= a*b*u + // |C[i][j]| <= a*b*(1+u) + C[i][j] += A[i][1]*B[1][j]; + // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u) + // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)² + C[i][j] += A[i][2]*B[2][j]; + // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²) + // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verify a loop induction, it becomes a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + + int k = 2; + // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // if OAPE(C[i][j]) <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)² + ``` + +Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than **[EM: more explanation on this is needed. (1) why is this a sound over-approximation and (2) how can you automatically compute it for an arbitrary implementation?]** + +$$\begin{array}{rcl} + |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\ + & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\ +\end{array}$$ + +where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$. + +### Unit Verification + +**[EM: incomplete section]** + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +* The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +* The `float` field is a symbolic abstract domain for the computed value. +* The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +# Formal specification guidelines + +*To be completed.* diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md new file mode 100644 index 00000000..e3c299eb --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/accuracy/accuracy-eric.md @@ -0,0 +1,481 @@ +# Objectives and limits + +## Introduction +This document provides guidelines to conduct the accuracy analysis of the SONNX numerical operators. + +SONNX does not provide a definitive specification of accuracy. Instead, it provides a way to estimate the error of a specific implementation with respect to an ideal algorithm that would implement integer arithmetic or floating point arithmetic according to IEEE754. + +Towards that goal, SONNX provides: +- an analytical expression of the upper bound of the error considering some ideal algorithm applying integer or IEEE 754 arithmetic +- a tool to estimate numerically an upper bound of the error of a given implementation. + +Notes: +1. There exist multiple ideal algorithms for any operator. In SONNX, we use the algorithm that is given in the informal specification. +2. There exists an infinite number of upper bounds of the error, some of them being more conservative (less accurate) than others. The accuracy of the estimation first depends on the simplifications done to facilitate calculus. It may also depend on the hypotheses done concerning the domain of the operator arguments. For instance, the estimation of the error bound for a matrix multiplication may be different under the hypothesis that one matrix is diagonal or under the hypothesis that both matrices are dense with a same order of magnitude for every coefficients.
+In SONNX, we estimate accuracies according to the following principles: + - we favour manageable formulas (simple and short) even if this leads to very conservative accuracy estimation + - we make no hypothesis about the domain of the arguments of operators. + + +# Principles of Numerical Accuracy Estimation + +The numerical error is the difference between the implemented algorithm $op_{\textit{impl}}$ applied on data with error resulting from previous approximated computations and an ideal algorithm $op_{\textit{ideal}}$ operating on data also coming from previous ideal computations: + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +We decompose the error into + +* a propagated error depending on the numerical + error and the numerical values of the inputs $-$ in particular, it is independent of the implementation and the storage format +* a introduced error depending on the + concrete value of the inputs and the implementation with its storage format. + +$$op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x}) = \textcolor{blue}{(op_{\textit{ideal}}(\overrightarrow{x+e}) - op_{\textit{ideal}}(\overrightarrow{x}))} + \textcolor{red}{(op_{\textit{impl}}(\overrightarrow{x + e}) - op_{\textit{ideal}}(\overrightarrow{x+e}))}$$ + +The error associated with the result of the operator corresponds to the sum of the propagated errors and the introduced error and this new error is then propagated by the next operator. + +In order to simplify the calculus and preserve the readability of the formulas, the provided estimations may result from the application of conservative simplifications of the native computer operations (e.g., IEEE 754). In some cases, additional hypotheses about the operator arguments (e.g., the fact that the matrices are diagonal) are done alongside the general formulation to provide more accurate estimations. + +The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries. + +## Error Propagation + +This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator. +**It is only for information**, since the properties does not depend on the implementation. The formula aims to explain how an input error is amplified by the operator just from its functional description. + +From the theoretical point of view, let us consider a function $f: \mathbb{R}^n \longrightarrow \mathbb{R}^n$ and +an existing error for each argument $x^i = (x^i_{\textit{val}}, x^i_{\textit{err}})$. + +### Estimation of the propagated error +The **propagated error** of the $f$ function as ideal operator is + +$$f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})$$ + +Hence if $f$ is derivable two times, the formula + +$$\sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) + \textit{ where } X_{\textit{err}} = \max(x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{err}}) \leq 1$$ + +is a correct propagated error with the natural following definition for $\mathcal{O}(X^2_{\textit{err}})$: + +$$\mathcal{O}(X^2_{\textit{err}}) = \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}$$ + +The absolute value of the propagated error is less or equal than + +$$\sum_{0 \leq i < n} \left| \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}}) \right| \times |x^i_{\textit{err}}|$$ + +### Example 1: Multiplication + +Let us consider the multiplication operation $f(x, y) = x\times y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$ + +For the multiplication, the **propagated error** $PE(f)$ is + +$$\begin{array}{rcl} +PE(f) & = & (x_{\textit{val}} + x_{\textit{err}})\times (y_{\textit{val}} + y_{\textit{err}}) - x_{\textit{val}}\times y_{\textit{val}} \\ + & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + x_{\textit{err}}\times y_{\textit{err}} +\end{array}$$ + +that is simplified into + +$$\begin{array}{rcl} +PE(f) & = & y_{\textit{val}}\times x_{\textit{err}} + x_{\textit{val}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +with $\mathcal{O}(X^2_{\textit{err}}) = x_{\textit{err}}\times y_{\textit{err}}$. + +Hence, + +$$\begin{array}{rcl} +|PE(f)| & \leq & |y_{\textit{val}}| \times |x_{\textit{err}}| + |x_{\textit{val}}|\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +### Example 2: Division + +Let us consider the division operation $g(x, y) = x / y$ in $\mathbb{R}\times\mathbb{R}\longrightarrow\mathbb{R}$ + +For the division, the **propagated error** $PE(g)$ is + +$$\begin{array}{rcl} +PE(g) & = & \frac{x_{\textit{val}} + x_{\textit{err}}}{y_{\textit{val}} + y_{\textit{err}}} - \frac{x_{\textit{val}}}{y_{\textit{val}}} \\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +that is simplified into + +$$\begin{array}{rcl} +PE(g) & = & \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}} + \mathcal{O}(X^2_{\textit{err}}) +\end{array}$$ + +The definition of $\mathcal{O}(X^2_{\textit{err}})$ is optional + +$$\begin{array}{rcl} + \mathcal{O}(X^2_{\textit{err}}) & = & \left(f(x^0_{\textit{val}} + x^0_{\textit{err}}, \ldots, x^{n-1}_{\textit{val}} + x^{n-1}_{\textit{err}}) - f(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\right) - \sum_{0 \leq i < n} \frac{\delta f}{\delta x^i}(x^0_{\textit{val}}, \ldots, x^{n-1}_{\textit{val}})\times x^i_{\textit{err}}\\ + & = & \frac{y_{\textit{val}}\times x_{\textit{err}} - x_{\textit{val}}\times y_{\textit{err}}}{y_{\textit{val}}(y_{\textit{val}} + y_{\textit{err}})} - \frac{1}{y_{\textit{val}}}\times x_{\textit{err}} - \frac{x_{\textit{val}}}{y^2_{\textit{val}}}\times y_{\textit{err}}\\ + & = & \frac{x_{\textit{val}}\times y^2_{\textit{err}} - y_{\textit{val}}\times x_{\textit{err}}\times y_{\textit{err}}}{y_{\textit{val}}^2(y_{\textit{val}} + y_{\textit{err}})} +\end{array}$$ + +Hence, the accuracy definition of the operator will just indicate that + +$$\begin{array}{rcl} +|PE(g)| & \leq & \frac{1}{|y_{\textit{val}}|}\times |x_{\textit{err}}| + \frac{|x_{\textit{val}}|}{y^2_{\textit{val}}}\times |y_{\textit{err}}| + |\mathcal{O}(X^2_{\textit{err}})| +\end{array}$$ + +### Example 3: 2D Matrix multiplication + +Tensor operators require a methodology to propose an **over-approximation of the propagated error**, since the algorithm combine many atomic operations on $\mathbb{R}$. + +1. Naïve Algorithm Description (as specified in SONNX informal specification) + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // OAPE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + // |PE(C[i][j])| <= a*be + b*ae + C[i][j] += A[i][1]*B[1][j]; + // |PE(C[i][j])| <= 2*a*be + 2*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // |PE(C[i][j]| = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j]| <= k*a*be + k*b*ae + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verify a loop induction, it becomes a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |PE(C[i][j])| <= k*a*be + k*b*ae + + int k = 2; + // if |PE(C[i][j])| <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |PE(C[i][j])| <= k*a*be + k*b*ae // same formula than the induction hypotheses + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' with an error whose absolute value is bound by 'ae' + // the absolute value of every coefficient of B is bound by 'b' with an error whose absolute value is bound by 'be' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // PE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |OAPE(C[i][j]| <= k*a*be + k*b*ae + // |PE(C[i][j])| <= p*a*be + p*b*ae + ``` + +3. Final specification + + At the end, we can specify that + + * If A is a matrix of dimension $n \times p$, B a matrix of dimension $p \times q$, + * if the absolute value of every coefficient of A is bound by $a$ with an error whose absolute value is bound by $ae$, + * if the absolute value of every coefficient of B is bound by $b$ with an error whose absolute value is bound by $be$, + * then the propagated error of every coefficient of C is bound by $p\times a\times be + p\times b \times ae$ + +## Error Introduction + +This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$, +the introduced error, where $Y$ is the tensor result of an operator. +The objective is to provide a specification that any implementation should respect. Hence + +$$Y_{\textit{err}}^{\textit{intro}} = op_{\textit{impl}}(\overrightarrow{x}) - op_{\textit{ideal}}(\overrightarrow{x})$$ + +From the theoretical point of view, the introduced error depends on the storage format of the result of any intermediate computation. +It is always defined as the difference between the implementation result and the ideal result, for which we additionaly +substract the propagated error of the previous section. + +The introduced error is an over-approximation of difference between the implementation result and the ideal result +when there is no error on the input. + +For IEEE-754 format, let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. For every floating-point operator +whose result $r$ is a normal floating-point number, the introduced error is less or equal than +$|r| \times \frac{\varepsilon}{2} = |r| \times \textit{\bf u}$ in the standard mode +round to nearest even. Moreover if $r \in [a, b]$ with $a$ and $b$ normal numbers (the interval +may contain denormal numbers), the introduced error is less or equal than +$\max(|a|, |b|) \times \textit{\bf u}$ in the standard mode round to nearest even. + +Note that there exists more accurate formulas, but by application of the principles stated introduction, we use a formulation that is remains simple and acceptably conservative. + +### Example 1: multiplication +Let us consider the multiplication operation: $f(x, y) = x * y$ + +The **introduced error** $IE(f)$ should be less or equal than + +$$\begin{array}{rcl} +|IE(f)| & \leq & |x|\times |y|\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$|x|\times |y|$ is a normal number + +$$\begin{array}{rcl} +|IE(f)| & \leq & \max(|a|, |b|)\times \max(|c|, |d|)\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + +$$\begin{array}{rcl} +|IE(f)| & < & \max(|a|, |b|)\times \max(|c|, |d|)\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d]$ and $\max(|a|, |b|)\times \max(|c|, |d|)$ is a normal number. + + +$$\begin{array}{rcl} +|IE(f)| & < & 1 +\end{array}$$ + +for any integer implementation. + +### Example 2: division +Let us consider the division operation: $g(x, y) = x / y$ + +The **introduced error** $IE(g)$ should be less or equal than + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{|x|}{|y|}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$\frac{|x|}{|y|}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & \leq & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\textit{\bf u} +\end{array}$$ + +for any floating-point implementation with the standard rounding mode round to nearest even, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & \frac{\max(|a|, |b|)}{\min(|c|, |d|)}\times\varepsilon +\end{array}$$ + +for any floating-point implementation with other rounding mode, provided +$x \in [a, b], y \in [c, d], 0 \not\in [c, d]$ and $\frac{\max(|a|, |b|)}{\min(|c|, |d|)}$ is a normal number + +$$\begin{array}{rcl} +|IE(g)| & < & 1 +\end{array}$$ + +for any integer implementation. + +### Example 3: 2D Matrix multiplication + +Tensor operators require a methodology to find an **over-approximation of the introduced error**, +since their algorithms combine many atomic operations. + +1. Naïve Algorithm Description + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + +2. Progressive decoration of the algorithm starting from inner loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + C[i][j] += A[i][0]*B[0][j]; + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + // |IE(C[i][j])| <= a*b*u + // |C[i][j]| <= a*b*(1+u) + C[i][j] += A[i][1]*B[1][j]; + // The introduced error is the one due to the multiplication + // at [0] and at [1]: a*b*u + a*b*u + // plus the one due to the addition: (a*b*(1+u)+a*b(1+u))*u + // |IE(C[i][j])| <= a*b*u + a*b*u + 2*a*b*(1+u)*u = a*b*u*(4+2u) + // |C[i][j])| <= (a*b*(1+u) + a*b*(1+u))*(1+u) = 2*a*b*(1+u)² + C[i][j] += A[i][2]*B[2][j]; + // |IE(C[i][j])| <= a*b*u*(4+2u) + a*b*u + (2*a*b*(1+u)² + a*b*(1+u))*u = a*b*u*(8+7u+2u²) + // |C[i][j]| <= (2*a*b*(1+u)² + a*b*(1+u))*(1+u) = (3+2*u)*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + By symplifying the formula, we progressively build a pattern that is candidate for a loop invariant + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 2; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + If the pattern verifies a loop induction, it becomes a loop invariant + +> Instead of "loop invariant", couln't we use the more "classical" terminology of induction reasoning? induction hypothesis (this is actually what you are using below) + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < 2; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + + int k = 2; + // if |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b + // and if |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // if OAPE(C[i][j]) <= k*a*be + k*b*ae + C[i][j] += A[i][k]*B[k][j]; + ++k; + // then |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // and then |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + for (int k = 3; k < p; ++k) + C[i][j] += A[i][k]*B[k][j]; + ``` + + Then the loop invariant enables to establish the post-condition when exiting the loop + + ```c++ + // A matrix of dimension n x p, B matrix of dimension p x q. + // C result matrix of dimension n x q + // the absolute value of every coefficient of A is bound by 'a' + // the absolute value of every coefficient of B is bound by 'b' + for (int i = 0; i < n; ++i) + for (int j = 0; j < q; ++j) { + C[i][j] = 0; + // IE(C[i][j]) = 0 + for (int k = 0; k < p; ++k) + C[i][j] += A[i][k]*B[l][j]; + // |IE(C[i][j])| <= (((1+u)^(k+1)-1)/u*(1+u)² - k-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(k+1)-1)/u*a*b*(1+u)² + // |IE(C[i][j])| <= (((1+u)^(p+1)-1)/u*(1+u)² - p-1)*a*b // same formula than the induction hypotheses + // |C[i][j])| <= ((1+u)^(p+1)-1)/u*a*b*(1+u)² + ``` + +Hence, for the matrix multiplication of A, matrix of dimension $n \times p$ with B, matrix of dimension $p \times q$, the **introduced error** should be less or equal than + +$$\begin{array}{rcl} + |IE(C[i][j])| & \leq & \left(\frac{(1+u)^{p+1}-1}{u}\times(1+u)^2 - p-1\right)\times a \times b\\ + & \leq & \left(\frac{(1+u)^{p+1}-1-(p+1)\times u }{u^2}\times(1+u)^2 - (p+1)\times(2+u)\right)\times a \times b \times u\\ +\end{array}$$ + +where the absolute value of every coefficient of A is bound by $a$ and the absolute value of every coefficient of B is bound by $b$. + + +> Are we sure to be able to do the same analysis for higher dimension tensors? + +## Unit Verification + +In the previous sections, we have described how to derive a possible error upper bound considering (i) the semantics of computer arithmetic in integers and floating point numbers and (ii) the algorithm used to define the operator in the informal and formal specifications. In this section, we propose a solution to evaluate the error upper bound on an actual C/C++ implementation. Examples are given for the reference implementation developed in SONNX. The same approach can be applied on a end-user implementation. + +The solution uses an abstract type SymbolicDomainError replacing each real number in the Why3 specification. SymbolicDomainError is a data structure with 4 fields: + +> Why do you refer to the specification? Should n't we write "replacing each floating point number in the implementation"? + +The real field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +The float field is a symbolic abstract domain for the computed value. +The err field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of float and real. +The rel_err field is a symbolic abstract domain for the relative error, that is the difference between the possible values of float and real divided by real. +Tensor A, B; + +> It would be nice to give links to the library (e.g., github with user manual). + +### Example : division + +> It would be nice to give the actual result of the analysis. + +```c++ +Tensor A, B; + +/* A, B symbolic initialization */ + +auto result = [&A,&B](auto I) { + return (B[I].real != 0) ? A[I] / B[I] : + /* undefined */ SymbolicDomainError::undef(); +}; + +for (auto I : A.indexes()) { + auto a = A[I]; + auto b = B[I]; + if (b.real != 0 && b.real + b.err != 0) { + auto c = result(I); + double bound = std::abs(a.err / b.real) + + std::abs(a.real * b.err / (b.real * b.real)); + assert(std::abs(c.err) <= bound + 1e-12); + } +} +``` + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md new file mode 100644 index 00000000..22a4f7c0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Jean.md @@ -0,0 +1,325 @@ +Reviewer: Jean Souyris + +# Introduction + +This document gives guidelines to be followed when writing an operator's +- informal specification +- formal specifications. + +# Informal specification + +> Remark 1: I propose to change the title "informal specification" to "Guidelines for the informal specification" or "Informal specification guidelines". Same remark for the future guidelines for the formal specification. + +>> OK: done + +> Remark 2: I propose to rename the two level-2 sub-sections "General recommendations" and "Structure" into "General guidelines" and "Structure of the informal specification" and to present them here as follows: +> +> This section is composed of two sub-sections: +> - "General guidelines", which expresses general recommendations, defines the use of specific fonts, some notations (e.g., for tensor) and tags (e.g., for constraints), and, finally, specifies how to deal with the numerical types involved in the operator at stake. +> - "Structure of the informal specification", which defines the structure and contents of the informal specification of an operator. This section makes use of the guidelines expressed in sub-section "General guidelines". + +>> OK: done. + +## General recommendations + +### Keep it simple! + +> Remark 1: I propose to change the title "Keep it simple" to "General recommendations" +>> OK: done + +> Remark 2: since we now have precise and detailed guidelines, this section should be adapted. Indeed, the simplicity should come from the strict application of the guidelines. The risk in keeping this section as it is, is to minimize the importance of the informal specification, e.g., part of the verification of the formal one will be performed against it. +> +> Proposal for the whole contents of this section: +> +> The informal specification is intended for readers who want to know how to use an operator, as well as for the ones who need to implement and verify a neural network from an ONNX model. For instance, the first kind of readers might be satisfied with one or two sentences about the semantics of an operator whereas the second category of readers would like to get all the details of the semantics. +> +> More precisely, the informal specification: + +> - Is aimed at showing clearly what a given operator is supposed to do, +> - Without calling on a strict formal, mathematical language, +> - Knowing that the exact and complete specification is given in the "formal" part. +> - May provide diagrams and examples to make things clear. + +> The writer of the informal specification of an operator shall have constantly in mind the following recommendation: "Keep it simple!", while applying the rules expressed in the guidelines. + +> Remove the following five bullets. +> +- Basically, the informal specification is a documentation, an "operator user's manual". +- It is aimed at showing clearly what a given operator is supposed to do, but without calling on a strict formal, mathematical language. +- The exact and complete specification is given in the "formal" part. +- The informal specification shall use greek with extreme parsimony ;-) +- The informal specification can provide diagrams and examples to make things clear. + +> End of proposal. + + +>> OK: done + + +### Fonts +- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`. + +### Notations + +> Remark 1: the term "shape" should be used instead of dimensions. + +>> OK: done + +- Tensors: + - A tensor is always represented in uppercase name (e.g., A, B,...,X, Y, Z). + - Input tensors are usually $A$, $B$,... + - Output tensor is $Y$ + - In the case of a variadic operator (e.g., "concat"), the tensor parameters are designated by an index: $X_0$, $X_1$, etc. Indexes start at 0 to be consistent with the other use of indexes. + - The dimensions of a tensor $X$ are denoted by a vector $(dX_0, ..., dX_i, ..., dX_n)$ where $dX_i$ refers to the dimension along axis $i$. The index of the first axis is 0. + - The numerical errors of a tensor $X$ are always represented by a tensor $X_{\textit{err}}$. + It is the difference between the tensor $X_{\textit{impl}}$ computed by an implementation + and the infinitly accurate tensor $X_{\textit{real}}$ expressed by the formal specification. + In the section on numerical accuracy, the notation $X_{\textit{real}}$ is replaced by $X$ unless it introduces ambiguity. + - For a tensor used as a variadic parameter (denoted $X_i$), the dimensions become $(dX_{i,0}, dX_{i,1}, ...)$. This notation is consistent with the way tensor dimensions are encoded in Why3. + - In cases where this naming convention does not match the one used by ONNX, a correspondence table may be established (e.g., $dX_2$ corresponds to the "width" of tensor $X$). + +### Tags +The informal specification makes use of three different types of tags: +- A **restrictions tag** expresses a restriction with respect to the ONNX standard (see the section about restriction below). They are indicated in the text with the tag `[R]` where `` is a number.\ +A synthesis of all restrictions is given in section "Restrictions" (see below). +- A **constraints tag** expresses a constraint on one or several inputs, output or attribute. They are indicated using `[C]` where `` is a number. +- A **traceability tag** identifies a specific location in the informal specification. These tags are used to establish a traceability between the informal and formal specification. They are indicated using `T` where `` is a number. + +> Remark 1: add [] around `T`. + +>> OK: I propose to remove the brackets and keep the non-serif font. + +> Remark 2: give an example of the declaration and use of a tag. + +>> OK: done + +### Types +- All operators applicable to numeric values shall be specified for values in the domain of real numbers. +- Specific description may be given for the other types (``float``, ``double``, etc.). +- A description can be applicable to a set of types as long as its **semantics description** remains the same for all types in the set. A counter example is, for instance, the case of operators applied on ``float`` or ``double`` that may create ``NaNs`` or ``+Inf`` or ``-Inf``. For this reason, they cannot be covered by the specification in $R$. +- Only the sections that need to be modified are repeated. + +> Remark 1: explicit the above sentence. See MatMul. + +>> OK: done + +## Structure + +> Remark 1: I propose to change the title of this section to "Structure of the informal specification" + +>> OK: done + +The specification on an operator is structured as follows. + +> Remark 2: introduce the "Contents" section as in MatMul. +> +> Proposal: + +> ### Contents + +> *This section lists the various per input set of types informal specifications of the operator at stake. Only the first line below is mandatory, i.e., the one for real inputs. Any subsequent line comes from the necessity to specialize the informal specification for the real to machine types* + +> - `` [operator (real)](#real) +> - `` [operator (comma-separated list of types)] +> - `` [operator (other comma-separated list of types)] +> - etc + +> Example (MatMul): +> +> Contents +- `MatMul` [operator (real)](#real) +- `MatMul` [operator (FP16, FP32, FP64, BFLOAT16)](#float) +- `MatMul` [operator (INT4, INT8, INT16, INT32, INT64, UINT4, UINT8, UINT16, UINT32, UINT64)](#int) + +> End of proposal. + +>> OK: done + +### Signature +*Definition of the operator's signature:* + + ` = (,,...)` + + where + - ``: <Brief description of the nth input > + - ``: <Brief description of output > + +### `` `()` + +> Remark 1: an introductory text should describe the contents of this kind of section by relating it to the Contents section above. + +#### Restrictions +*This section lists all restrictions applicable to the operator. A restriction is a limit with respect to the normal usage domain of the ONNX operator. A restriction may concern the dimension of tensors, values of attributes, etc. They are introduced to simplify the implementation of operators, ensure resource usage predictability, enforce explicitness, etc.* + +> Remark 1: the verb "simplify" and, later in this section, the notion of "simplification" appear either inside the notion of "restriction" or aside this notion. Furthermore, the term "simplification" might be misinterpreted by the readers. Therefore I propose not to talk about "simplification" as a category but to split the notion of restriction into two categories: +> - "Dependability restrictions": the restrictions for dependability reasons. +> - "Other restrictions": the restrictions that aim at limiting the operator specification effort for the first delivery of SONNX. The restrictions of this second category are acceptable only if they do not prevent the operator at stake from being used in a sufficiently large domain. + +>> OK. See the proposed text. + + +*An example is given hereafter* + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `[R1]` | Input tensor `X` has 2 spatial axes | Simplification | +| `[R2]` | Attribute `auto_pad` is restricted to `NOTSET` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | + +*We discriminate **simplifications** from **restrictions*** as follows: +- Simplifications are introduced to reduce the amount of work for the workgroup and is aimed at being eventually removed. They are not traceable to a requirement. + +> Remark 2: see remark 1 in this section. I propose to "merge" the text into the Remark 1 proposal. +> +> Remark 3: give an example of "Other restrictions". + +>> OK: See new text. + +- Restrictions are introduced to comply with some requirement. The requirement is identified using an hyperlink. + + + ##### Informal specification + + *This section contains the informal specification of the operator. By "informal", we mean that the description does not rely on a formal language, even though it will usually include mathematical formulae. The specification shall be readable, understandable, and self-contained. It can include figures if deemed necessary. The objective is that a human being can fully understand the domain, range, and semantic of the operator with no additional information. Stated differently, he/she should be able to implement the operator with no additional information.* + +> Remark 1: a more precise template should be proposed for this section. Indeed, some of the remarks and exchanges we had about the informal specification of concat, including the ones with Loïc, should be introduced here. + +> Proposal: +> +> The informal specification shall be composed of the following parts: + +> - One or two sentences that give a synthesis of what the operator does + + Example: "Operator conv computes the convolution of the input tensor X with the kernel W and adds bias B to the result. Two types of convolutions are supported: standard convolution and depthwise convolution." + +> - The mathematical description that: +> - Uses the notations proposed in section Notations of these guidelines +> - Implements the traceability tags proposed in Section Tag of these guidelines +> - Structures the mathematical formulae according to the following pattern: the complete formula is first given and its atomic elements and sub-expressions are defined afterward, by introducing them with "Where" or "In which" + +> Example of mathematical description (convolution): + +$$\begin{gathered} + Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) + B[c] +\end{gathered}$$ + +> Where +- >$b$ is the batch index, $b \in [0,b(Y)-1]$, $b(Y)$ is the batch size of output `Y` +- > $c$ is the data channel, $c \in [0,c(Y)-1]$, $c(Y)$ is the number of data channels of output `Y` +- > etc + +##### Error conditions + +*This section identifies the errors that may occur during the execution of the operator (or *runtime errors*).* + +When writing a specification, the writer must identify the conditions where the following conditions may occur: +- for floating point computations + - an invalid operation as defined in IEEE 754 section 7.2, i.e. + - multiplication $(0, \infty)$ or multiplication $(\infty, 0)$ + - addition or subtraction or fusedMultiplyAdd: magnitude subtraction of infinities, such as addition $(+\infty, -\infty)$ + - division $(0, 0)$ or division ($\infty, \infty)$ + - remainder(x, y), when y is zero or x is infinite and neither is a NaN + - square root if the operand is less than zero + - etc. Refer to the standard for a complete list of error conditions. +- for integer computations + - division by zero, + - operations leading to a value out of the range (e.g., addition of two large `int32` values non representable in `int32`) + +This section shall indicate if an operator can potentially return a `NaN` or `Inf`. It is is the case, the condition that might lead to this situation must be described. + +If the section is left empty, it means that **not error condition can occur**. + +##### Inputs + +This section describes the operator's inputs. + +###### ``: `` + +where `` is the name of the input and `` is the type of the input. + +##### Attributes + +- This section describes the operator's attributes. +- It also gives all constraints applicable to the input/output/attribute (if any). +- When a constraint involves several inputs/outputs/attributes, it is only be described for the first one and will be cross-referenced in the "constraint" section of the other ones..\ +The description is structured as follows:* + + - (C<i>) <Title of constraint> + - Statement: <Expression of the constraint> + - Rationale: <Justification for the constraint> + +###### ``: `` + +where `` is the attribute's name and `` is the attribute's type. + + ###### Constraints + +*(same as above)* + + ##### Output + + This section describes the operator output. + + ###### ``: `` + +where `` is the output's name and `` is the output's type. + + + ###### Constraints + + *(same as above)* + + ##### Formal specification + +This section contains a link to the formal specification expressed in Why3. + +##### Numerical Accuracy + +This section provides a tight and verifiable specification of the numerical error +on the operator's results. It decomposes the error into two parts: +the first, the propagated error, depends on the numerical error and the +numerical values of the inputs ; the second part, the introduced error, +depends only on the numerical value of the inputs. + +The provided specification results from an over-approximated semantics (ex: IEEE-754) of the +numerical error of native computer operations approximating real number +operations. In order to preserve the readability of the formulas, the general specification +introduces additional (conservative) simplifications compared to the original spectifiations. +However, this general specification may be too over-approximated for some specific inputs +(ex tensor representing diagonal matrices). In this case, more precise specific specifications +are provided alongside the general specification. + +The error specification comes with unit verification scenarios to verify the implementation's +conformity. In the absence of value ranges for the inputs, the unit verification scenarios +operate on symbolic values and errors to propagate correct formulas throughout the scenario +and thus provide a proof for the assertions. In particular, the C implementation generated +from the Why3 formal specification must be verified using these scenarios, for example +by using symbolic instrumentation libraries. + +###### Error Propagation + +This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the +propagated error, where $Y$ is the tensor result of an operator. + +###### Error Introduction + +This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$, the +introduced error, where $Y$ is the tensor result of an operator. + +Hence $Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +###### Unit Verification + +This section contains a verification scenario to verify the above specification +for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each +real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +* The `real` field is a symbolic abstract domain for ideal (infinitly precise) C/C++ floating-point + (or fixed-point) computations. +* The `float` field is a symbolic abstract domain for the computed value. +* The `err` field is a symbolic abstract domain for the absolute error, that is the difference + between the possible values of `float` and `real`. +* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference + between the possible values of `float` and `real` divided by `real`. + + + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Salome.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Salome.md new file mode 100644 index 00000000..6cad619e --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/informal/guidelines_review_Salome.md @@ -0,0 +1,231 @@ +Reviewer: Salome Marty Laurent + +# Introduction + +This document gives the guidelines to be followed when writing an operator's **informal** and **formal** specification. + +Nota: as of July 2025, guidelines are limited to the *informal* specification. + +# Informal specification guidelines +This section is composed of two sub-sections: +- *General guidelines*, which defines the use of fonts, notations (e.g., for tensor) and tags (e.g., for constraints), and, finally, specifies how to deal with the numerical types involved in the operator at stake. +- *Structure of the informal specification*, which defines the structure and contents of the informal specification of an operator. This section applies the general guidelines. + +## General guidelines +The informal specification is intended for both users *and* implementers of operators who both need to understand what an operator does and how to use it. For instance, the first kind of readers might be satisfied with one or two sentences about the semantics of an operator whereas the second category of readers would like to get all the details of the semantics. + +More precisely, the informal specification: +- Is aimed at showing clearly what a given operator is supposed to do, +- Without calling on a strict formal, mathematical language, +- Knowing that the exact and complete specification is given in the "formal" part. +- May provide diagrams and examples to make things clear. + +The writer of the informal specification must take care to keep it readable and understandable by a ML developer. The recommendations given in the following guidelines target this objective. + +### Fonts +- Inputs, outputs, and attributes are represented using a non-serif font. For instance, the "pads" attribute is represented by `pads`. + +### Notations +#### Tensors +- A tensor is always represented in uppercase letters (e.g., A, B,...,X, Y, Z). +- Input tensors are usually named $A$, $B$,... In cases where this naming convention does not match the one used by ONNX, a correspondence table may be established (e.g., $dX_2$ corresponds to the "width" of tensor $X$). +- Output tensor is usually name $Y$ +- In the case of a variadic operator (e.g., "concat"), the tensor parameters are designated by an index: $A_0$, $A_1$, etc. Indexes start at 0 to be consistent with the other use of indexes. +- The shape of a tensor $A$ is denoted by a vector $(dA_0, ..., dA_i, ..., dA_n)$ where $dA_i$ refers to the dimension along axis $i$. The index of the first axis is 0. +- For a tensor used as a variadic parameter (denoted $A_i$), the shape is denoted by $(dA_{i,0}, dA_{i,1}, ...)$. +#### Errors +- The numerical errors of a tensor $A$ are always represented by a tensor $A_{\textit{err}}$ that is the difference between the tensor $A_{\textit{impl}}$ computed by some implementation and the infinitely accurate tensor $A_{\textit{real}}$ expressed by the formal specification for real numbers. + - In the section on numerical accuracy, the notation $A_{\textit{real}}$ is replaced by $A$ unless it introduces ambiguity. + +#### Tags +The informal specification makes use of three different types of tags: +- A **restrictions tag** expresses a restriction with respect to the ONNX standard (see the section about restriction below). They are indicated by tag `R` where `` is a number.\ +A synthesis of all restrictions is given in section "Restrictions" (see below). +> Remark: The constraints tag should be used for references, similar to the traceability tag. When referencing constraints, I suggest using absolute numbering. +- A **constraints tag** expresses a constraint on one or several inputs, outputs, or attributes. They are indicated using `C` where `` is a number. +> Remark: I propose to differentiate the reference and definition of such traceability tag. The definition expressed as "`T`**:**" and the reference as "`T`". +- A **traceability tag** identifies a specific location in the informal specification. These tags are used to establish traceability between the informal and formal specifications. They are indicated by tag `T` where `` is a number. + +For instance, here is a tag introducing a constraint relating the input and output tensors for the `Abs` operator: +>`C1` Shape consistency \ +> Statement: Tensor $A$ and $Y$ shall have the same shape. + +### Types +- All operators applicable to numeric values shall be specified for values in the domain of real numbers. +- Specific description may be given for the other types (`float`, `double`, etc.). +- A description can be applicable to multiple types as long as its **semantics description** remains the same for all types. A counter example is, for instance, the case of operators applied on `float` or `double` that may create `NaNs` or `Inf`s. For this reason, they cannot be covered by the specification in $\mathbb R$. +- In order to reduce the size and complexity of the specifications, the description of the semantics of an operator for type T may refer explicitly to the semantics of the same operator for another type T' (typically: the semantics in $\mathbb R$). + +## Structure of the informal specification + +The specification on an operator is structured as follows. + +### Contents + +This section gives the list of all informal specifications of the operator, for each of the applicable types. + +- `` operator for type real +- `` operator for types `` +- `` operator for types `` +- etc + +Here is an example for operator `MatMul`: +> Contents +>- `MatMul` operator for type real +>- `MatMul` operator for types `FP16`, `FP32`, `FP64`, `BFLOAT16` +>- `MatMul` operator for types `INT4`, `INT8`, `INT16`, `INT32`, `INT64`, `UINT4`, `UINT8`, `UINT16`, `UINT32`, `UINT64` + +The following section must be repeated for each set of types for which the semantics is the same. One section corresponds to one entry in the "Contents" list. + +## `` `()` + +### Signature + +Definition of the operator's signature: + + ` = (,,...)` + + where + - ``: <Brief description of the nth input > + - ``: <Brief description of output > + +#### Restrictions +This section lists all restrictions applicable to the operator. A restriction is a limit with respect to the normal usage domain of the ONNX operator. A restriction may concern the dimension of tensors, values of attributes, etc. + +Restrictions marked as "Transient" are introduced by the working group in order to reduce the specification effort. Such restrictions, which are not traceable to a need, are normally aimed at being eventually relaxed. However, in the meantime, both transient and non-transient restrictions are applicable by the operator user or implementer. + +Restrictions not marked as "transient" are traceable to some requirement. The requirement is identified using an hyperlink. + +An example is given hereafter + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Input tensor `X` has 2 spatial axes | Transient | +| `R2` | Attribute `auto_pad` is restricted to `NOTSET` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | + + #### Informal specification + + This section contains the informal specification of the operator. By "informal", we mean that the description does not rely on a formal language, even though it usually uses some mathematical formulae. The specification shall be readable, understandable, and self-contained. It can include figures if deemed necessary. The objective is that a human being can fully understand the domain, range, and semantic of the operator with no additional information. Stated differently, he/she should be able to implement the operator with no additional information. + +The informal specification shall be composed of the following parts: +- A short description of the operator. For instance, for the `conv` operator: + +> Operator `conv` computes the convolution of the input tensor $A$ with the kernel $W$ and adds bias $B$ to the result. Two types of convolutions are supported: standard convolution and depthwise convolution. + +- A detailed description that: + - Uses the notations proposed in section "Notations" of these guidelines + - Implements the traceability tags proposed in Section "Tags" of these guidelines + - Presents the mathematical formulae, if necessary, according to the following pattern: the complete formula is first given and its atomic elements and sub-expressions are defined afterward, by introducing them with "Where" or "In which" + +For instance, for the `Conv`operator: + +> $$\begin{gathered} + Y[b, c, m, n] = \sum_{i=0}^{fm(W)-1} \sum_{j=0}^{h(W)-1} \sum_{z=0}^{w(W)-1} (X[b,i,m \cdot strides[0]+ j \cdot dilations[0], n \cdot strides[1]+ z \cdot dilations[1]] \cdot W[c, i, j, z]) + B[c] +\end{gathered}$$ +> Where +>- $b$ is the batch index, $b \in [0,b(Y)-1]$, +>- $b(Y)$ is the batch size of output `Y` +>- $c$ is the data channel, $c \in [0,c(Y)-1]$, +>- $c(Y)$ is the number of data channels of output `Y` +>- etc + +#### Error conditions + +This section identifies the errors that may occur during the execution of the operator (or *runtime errors*). + +When writing a specification, the writer must identify the conditions where the following conditions may occur: +- for floating point computations + - an invalid operation as defined in IEEE 754 section 7.2, i.e. + - multiplication $(0, \infty)$ or multiplication $(\infty, 0)$ + - addition or subtraction or fusedMultiplyAdd: magnitude subtraction of infinities, such as addition $(+\infty, -\infty)$ + - division $(0, 0)$ or division ($\infty, \infty)$ + - remainder(x, y), when y is zero or x is infinite and neither is a NaN + - square root if the operand is less than zero + - etc. Refer to the standard for a complete list of error conditions. +- for integer computations + - division by zero, + - operations leading to a value out of the range (e.g., addition of two large `int32` values non representable in `int32`) + +This section shall indicate if an operator can potentially return a `NaN` or `Inf`. It is is the case, the condition that might lead to this situation must be described. + +If the section is left empty, it means that **not error condition can occur**. + +#### Inputs + +This section describes the operator's inputs. + +##### ``: `` + +where `` is the name of the input and `` is the type of the input. + +###### Constraints +This section gives all constraints applicable to the input. + +- When a constraint involves several inputs/outputs/attributes, it is only described once when the first input or attribute concerned by the constraint is described. Then, for the other inputs, attributes, or outputs concerned by the same constraint, a cross-reference is given. +The description is structured as follows:* + + - `C` <Title of constraint> + - Statement: <Expression of the constraint> or cross-reference to the previous location where this constraint was first introduced. +> Remark: Considering its absence in some informal specifications, is the "Rationale" section necessary ? + - Rationale: <Justification for the constraint> + +#### Attributes +This section describes the operator's attributes. + +##### ``: `` +where `` is the attribute's name and `` is the attribute's type. + + ##### Constraints +Same as for the inputs. + + #### Output + This section describes the operator output. + + ##### ``: `` + +where `` is the output's name and `` is the output's type. + + ##### Constraints +Same as for the inputs. + + #### Formal specification + +This section contains a link to the formal specification expressed in Why3. + +#### Numerical Accuracy + +This section provides a tight and verifiable specification of the numerical error +on the operator's results. It decomposes the error into two parts: +the first, the propagated error, depends on the numerical error and the +numerical values of the inputs ; the second part, the introduced error, +depends only on the numerical value of the inputs. + +The provided specification results from an over-approximated semantics (ex: IEEE-754) of the +numerical error of native computer operations approximating real number +operations. In order to preserve the readability of the formulas, the general specification introduces additional (conservative) simplifications compared to the original specifications. +However, this general specification may be too over-approximated for some specific inputs (ex tensor representing diagonal matrices). In this case, more precise specific specifications are provided alongside the general specification. + +The error specification comes with unit verification scenarios to verify the implementation's conformity. In the absence of value ranges for the inputs, the unit verification scenarios operate on symbolic values and errors to propagate correct formulas throughout the scenario and thus provide a proof for the assertions. In particular, the C implementation generated from the Why3 formal specification must be verified using these scenarios, for example by using symbolic instrumentation libraries. + +###### Error Propagation + +This section contains tight properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of an operator. + +###### Error Introduction + +This section contains tight properties of $Y_{\textit{err}}^{\textit{intro}}$, the introduced error, where $Y$ is the tensor result of an operator. + +Hence $Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +###### Unit Verification + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +* The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +* The `float` field is a symbolic abstract domain for the computed value. +* The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +* The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +# Formal specification guidelines + +*To be completed.* \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/SONNX - Test strategy V2 Notes JLF.pdf b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/SONNX - Test strategy V2 Notes JLF.pdf new file mode 100644 index 00000000..9f066ffc Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/SONNX - Test strategy V2 Notes JLF.pdf differ diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/tests_additional _material.md b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/tests_additional _material.md new file mode 100644 index 00000000..8c335a9d --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/reviews/tests/tests_additional _material.md @@ -0,0 +1,62 @@ + +#### Testing against shapes +For each operator, there shall be at least one test showing the correct behaviour of the operator for the following tensor shapes (when applicable): +- scalar tensor (rank-0 tensor) +- 1D tensor +- 2D tensor +- 3D tensor +- one nD- tensor with n>3 +- For each of the previous tensor shapes, there shall be at least one test in which at least one dimension of the tensor is null (null tensor) +#### Testing against indexes +- For a given shape, each index +#### Selection of values +- A test must be designed to as to discriminate correct behaviours from incorrect ones. This concern both the mathematical operation itself (e.g. using zeroes or infinities for the two arguments of a $+$ and a $\times$ ) and the values of indexes. So the value of the tensor must be chosen so that using an incorrect index $i_\textit{err}$ instead of $i$ lead to a result that can be discriminated from the correct one. A necessary condition is that the value at multi-index $j$ in the output can only be computed from a unique combination of input values at index $i_1$, $i_2$, ... , $i_n$ for an opertor with $n$ arguments. + For instance, when doing the pointwise addition `[[1 2][3 4]] + [[1 2][3 4]]=[[2 4][6 8]]`, values 4 can be obtained by adding 1+3 or 2+2. A better test would be `[[1 2][3 4]] + [[100 200][300 400]]=[[101 202][303 404]]` + For the **Mult** operator, if one value in the first argument is zero, then the value of the result will be zero whatever the value of the second argument. + +>[!Note] Those tests do not prevent bad implementation to pass the test. They just prevent badly chose input values to mask errors. + +- There shall be one test showing the correct behaviour of the operator with respect to the IEEE special values (-inf, -0, +0, +Inf, NaN), for all combinations. +#### Test of pooling +When an operator uses pooling, test shall be done +- for pooling values null and non null in all dimensions : + - for kernel sizes such that the dilated kernel is smaller and equal to the size of the padded argument (\*) : + - for values of the argument and kernel showing the use of correct padding values. For instance, for the **MaxPool** operator, the padding value should (conceptually) be -inf. So a test shall be done with values of the argument equal to -inf and a padded argument in all dimensions (e.g., for a 2D tensor: padding is (1,1,1,1)) + +(\*) Other cases are covered by the pre-condition tests +#### Test of striding +When an operator uses striding, test shall be done at least +- for all dimensions : + - for striding value equal to 1 and to the dimension of the tensor on which striding is performed + +### Test of Structural operators +A "structural operator" is an operator that does not computes new values from the input arguments but reorganizes the values given in input to build the output tensor. Operators **Flatten** or **Reshape** is are examples of structural tensors. + +The test of structural operator much show that the value of the output tensor for a given multi-index $j$ actually comes from the correct entry $i$ in the input tensor. Such operator implement a relation between $i$ and $j$ and tests must show that this relation is correct (for the operator). +But, the relation between multi-indexes is not since the operator only manipulates values, the multi-indexes relation is not observable. For instance, if all the entries of the input tensor contains the same value, all mappings between $i$ and $j$ are undistinguishable (hence, non testable). + +Therefore, to test structural tensor, each entry of the input tensor must hold a value that can be traced to a unique entry in the output tensor. + +For instance, for an 2x3x4 inputs tensor, of the **Flatten** operator, +``` +Axis=0 +Shape of input tensor: (2, 3, 4) +X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]] +Shape of output tensor: (1, 24) +Result = [[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]] +``` + +Each entry of the result can be traced to a specific entry in the input and the correct begaviour of the operator can be assessed. + +### Tests against constraints + +- For each constraint (Ci) taken separately, and each variable $v$ appearing in the constraint, there shall be at least one showing the effect of the variable on the satisfaction/violation of each constraint **if the behaviour of the operator is described for those violations**. If the behaviour is not decsribed, thi smeans that the beaviour of the operator is undefined and no test can be described. + For instance, in the case of **MaxPool** operator, the following constraints are applicable (2D case): + - padding values must be positive: $$\forall i \in {0,1,2,3}: \textit{pad\_shape}[i] \ge 0$$ + - striding values must be strictly positive: $$\forall i \in {0,1}: \textit{stride}[i] \gt 0$$ + - for each spatial dimension, the dilated kernel must fit within the padded input, i.e.: $$dX2+\textit{pad\_shape}[0]+\textit{pad\_shape}[2] \ge \textit{dilations}[0]×(\textit{kernel\_shape}[0]−1)+1$$ and $$dX3+\textit{pad\_shape}[1]+\textit{pad\_shape}[3] \ge \textit{dilations}[1]×(\textit{kernel\_shape}[1]−1)+1$$ + - the size of the left (resp. top) and right (resp. bottom) padding must be strictly smaller than the size of the dilated kernel, e.g., for a tensor with two spatial dimensions $$\forall i \in {0,1,2,3}: \textit{pad\_shape}[i] \lt \textit{dilations}[0]×(\textit{kernel\_shape}[i]−1)+1$$ + - the $\textit{pad\_shape}$ argument shall provide padding values for the beginning and end of each spatial dimensions, i.e. $$d\textit{pad\_shape}0=2\times (dX1+dX2+\ldots+dXi+\ldots+dXn)$$ + - The shape of the dilation tensor shall be compatible with the shape of the kernel: $$s\textit{dilation}=sW$$ + - The shape of the output tensor shall comply with the shape of the computed max pool, i.e. $$dY2​=⌊(dX2​+\textit{pad\_shape}[0]−\textit{dilations}[0]×(\textit{kernel\_shape[0]}−1)−1)/(\textit{strides}[0]+1)⌋$$ and $$dY3=⌊(dX3+pad_shape[1]−dilations[1]×(kernel_shape[1]−1)−1)/(strides[1]+1)⌋$$ + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/sonnx_user_manual.md b/safety-related-profile/sonnx/ops/docs/guidelines/sonnx_user_manual.md new file mode 100644 index 00000000..25ab8f0c --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/sonnx_user_manual.md @@ -0,0 +1,73 @@ +# Introduction + +## Overview of the SONNX profile operator specification + +## Preamble + +### Definitions +The [following definitions](../../spec/informal/common/definitions.md) are applicable for all operations. + +### Restrictions +The [following restrictions](../../spec/informal/common/general_restrictions.md) are applicable for all operators. + +### Notations +The [following notations](../../spec/informal/common/notations.md) are applicable for all operators. + +# The structure of a specification + +A specification is organized as follows: +- Table of contents with hyperlinks organized according to the datatypes supported by the operator. A specification for real numbers is systematically given (see later in this document). +- Specification for type \ + - Signature of the operator giving the type of the arguments and outputs + - Applicable restrictions applicable to the SONNX profile + - Functional specification including illustrations and examples when needed + - Error conditions + - Attributes (if applicable) + - Inputs + - Outputs + +Section "Specification for type \" is repeated for all datatypes suppted by the operator. If the specification for type \ is strictly identical to specification for type \, then section \ may simply refer to section \. + +# Specific points + +## Restrictions + +*To be completed.* + +## The "real" type + +In order to provide the simplest specification of each operator, SONNX specifies each operators for real numbers ($\mathbb{R}$). This allows giving a simple mathematical specification of operators without having to care about the peculiarities of computer arithmetic (IEEE754's floating point special values such as NaN, +Inf, etc., integer overflows, etc.). + +When the specification of a computer data type strictly follows the semantics of the operator in $\mathbb{R}$, it may directly refer to the real number section. Structural operators that do not perform any arithmetic operation are a specific case where all operators refer to the specification for Real numbers. + +## Requirement tags + +Specification items are enclosed between the following tags: + +[E\_\\_\\_\\_]
+ +*Some specification text...* + +[END]
+ +where +- [] designates the operator +- [] designates the data type (real, float, int, etc.) +- [] designates the requirement category among + - CONSTRAINT for a constraint specificition item + - FUNC for a functional specification item +- [] is a 5-digit numerical id. + +Several exception apply +- Constraints are not closed by a [end] tag. Constraints are listed using a "bullet list" in which one item is one (tagged) requirement. +- The signature paragraphs are requirements *by right*. They are not tagged because they can be referred to using the section number. +- If section \ refers to section \ in order to avoid redundancy, tags [E\_\\_T'\_\\_]
+ is considered to be implicitly defined, and with the same specification text as for [E\_\\_T\_\\_]
. + +## Basic operators + +When considering real numbers, some operators are considered "well-known" and not specified any further. This is the case for instance of the four basic operators ($+,-,\times,/$), trigonometric operators ($sin(x)$, $cos(x)$, etc.), square root ($\sqrt x$), etc. + +For computer datatypes such as `float`, `double`, `int8`, `uint8`, etc., primitive operators (**Add**, **Sub**, **Mul**, **Div**) are fully specified and the coresponding operator ($+,-,\times, /$) refer to those operators. For instance, expression $x+y$ for `int8` actually refers to **Add(x,y)**. By extension, this also applies to iretaive sums ($\sum$) and products ($\prod$). + + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/tests-part1.md b/safety-related-profile/sonnx/ops/docs/guidelines/tests-part1.md new file mode 100644 index 00000000..98727f35 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/tests-part1.md @@ -0,0 +1,174 @@ + +# Purpose of tests in SONNX + +In SONNX, functional tests are provided to check consistency between the SONNX specification and some existing implementations of the ONNX standard. +As of Feb 2026, tests are performed against ONNX runtime (Python API, x64 architecture, default CPU, version ). + +The objective is to challenge our specification against de facto standard implementation, in addition to the inspection and review process. No conclusion can be drawn on the quality of the specification if test passes, but if tests do not pass, this may reveal a problem (either in the specificaiton or in the implementation). +In addition, in cases where specificiers are faced to choices, tests can also serve as a means to ensure that the SONNX specification remains as much as possible aligned with them. + +Out of SONNX, those tests can be used, for instance, to verify a user-implementation of the SONNX specification against the SONNX reference implementation (back-to-back testing). + +Note that only **input** values are defined. Expected output values are those produced by the reference implementation. + +# Disclaimer +The test developed by SONNX are **functional tests** aimed at covering the function realized by operators, with no consideration about their actual implementation. + +Those tests may reused as part of users' verification activities, but with no guarantee of completeness. It is the responsibility of the user to ensure that his/her test set complies with his/her test objectives. + +# Test strategy + +This section defines the strategy that must be applied to define SONNX tests. + +## Definitions and abbreviations +- dut: device/unit under test (the ONNX operator implementation function) +- oracle: trusted mechanism to determine expected output +- ulp: unit in the last place (floating-point comparison measure) +- special values: IEEE-754 values including ±0, ±inf, NaN, subnormals +- rbt: requirements-based testing +- robustness testing: tests beyond nominal requirements to show predictable behavior under abnormal conditions + +## Configuration +For any test, the following information must be provided: +- opset (the one indicated in the specification) +- datatype +- full chacterization of the test execution platform including + - compile-time options affecting numerics (*to be completed:* fast-math, fused ops, SIMD, etc.) + - hardware target (x86_64) + - operating system (linux) + - compiler version + - floating-point mode: default IEEE-754 rounding mode (round to nearest ties to even), denormal handling policy (e.g., preserve subnormals or flush-to-zero) + - environment (python version, library versions,...) + + +## Reproducibility +Tests must be reproducible. + +The following elements must be defined precisely +- random seeds for randomized tests or test of random operators +- non deterministic execution order that would raises issues related to the non-associativity of floating point operations +- single-thread in unit tests unless testing determinism explicitly) + +## Verification objectives +For each operator and each supported datatype/opset/attribute variant, demonstrate: +- correctness: outputs match specification (*to be completed*: what is the acceptable error margin for a test to pass) +- robustness: predictable failure mode on invalid inputs (no crashes, no undefined behavior) +- completeness: all requirements tested; traceability exists + +## Requirement model +As of today, the SONNX specification is not expressed in terms of atomic and well-identified requirement. +Therefore, as much information as possible shall be provided to link the test to the specification (e.g., line number?) + +## Test approach and levels +### Level a: conformance (requirements-based) +- nominal functional tests for each datatype and opset variant +- attribute tests including defaults and boundary values +- broadcasting coverage +- shape and rank coverage + +## Level b: numerical robustness (floating pints) +- ieee special values, overflow/underflow edges, cancellation scenarios +- stable tolerance rules (absolute/relative/ulp) defined in section 10 + +## Level c: integer robustness +- min/max boundaries, overflow semantics, division by zero, shifts, saturate vs wrap +- quantization-specific tests if relevant + +## Level d: property-based testing (invariants) +- randomized inputs with invariants per operator +- used to discover corner cases; does not replace requirements-based tests + +## Level e: negative tests (robustness beyond requirements) +- invalid dtype combinations, invalid ranks, invalid attribute combinations +- aliasing and overlapping buffers if supported + +## Pass/fail criteria + +### Comparisons for integer values +- exact match required for integers and booleans unless the spec defines otherwise + +### Comparisons for floating point values +Define the project-wide float comparison policy, per operator category. + +#### Mathematically exact ops (e.g., structural operations such as reshape, transpose) +- exact (bitwise comparison) + +#### General elementwise arithmetic: compare with max(abs_err, rel_err, ulp_err) policy +- abs_err_threshold: default 1e-6 for float32, 1e-12 for float64 (adjust per op) +- rel_err_threshold: default 1e-5 for float32, 1e-12 for float64 +- ulp_threshold: default 2 ulp for float32, 4 ulp for float16 if supported + +#### Numerically sensitive ops (e.g., softmax, log, exp, normalization) +Use tighter spec-driven criteria such as: +- softmax: sum(output) within 1e-5 of 1 for float32; all outputs in [0,1] except NaN cases +- log/exp: handle saturation near overflow with expected inf behavior + + +## Test design rules (tricky cases coverage) +For every operator, generate tests covering the cartesian products of the following attributes +- dimension class + - scalar + - 1d + - 2d (matrix) + - nd (rank 3–Nmax) + +- shapes + - same shape + - broadcasting: + - trailing broadcast (e.g., [2,3,4] + [4]) + - middle broadcast (e.g., [2,3,4] + [1,3,1]) + - scalar broadcast + - degenerate dims: include size-1 dims, empty dims if allowed by spec/runtime + - large dims near capacity (stress subset) + +value classes +- zeros (including +0 and -0 for float) +- ones +- small magnitudes (near epsilon) +- large magnitudes (near max normal) +- alternating signs +- random uniform +- adversarial patterns (monotonic sequences, repeated values) + +special ieee values (float) +- NaN, +inf, -inf +- subnormals +- max/min normals + +integer boundaries +- min, max, -1, 0, 1 +- near overflow (max-1, max) +- division by zero inputs +- shift amounts: 0, 1, bitwidth-1, bitwidth, >bitwidth (expected error or masked behavior per spec) + +attributes coverage +- default attribute path +- each attribute min/max +- invalid attribute values produce error +- conflicting attribute combos produce error + +error handling +- invalid dtype combinations +- invalid ranks +- incompatible shapes +- empty tensors where applicable + + +## Test case template +Each test case shall be documented with the following fields: + +- test id: tc----### +- operator: +- opset: +- datatype(s): +- input shapes: +- attributes: +- input values: (explicit vectors or generation rule) +- oracle: (onnxruntime / numpy / analytic formula / secondary oracle) +- expected output: (explicit or derived) +- comparison rule: (exact / abs+rel / ulp) +- expected errors: (if negative test) +- part of the specification covered +- notes: (special handling such as NaN, denormals, flush-to-zero) + + diff --git a/safety-related-profile/sonnx/ops/docs/guidelines/tests-part2.md b/safety-related-profile/sonnx/ops/docs/guidelines/tests-part2.md new file mode 100644 index 00000000..ad5256f4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/guidelines/tests-part2.md @@ -0,0 +1,400 @@ +```table-of-contents +title: **Table of contents** +hideWhenEmpty: true +``` +## Introduction +This document proposes an approach to develop tests of the SONNX operators. + +Note that it does not (yet) cover the actual implementation of tests (using [Hypothesis](https://hypothesis.works/) or any other test framework such as [pytest](https://docs.pytest.org/en/stable/), [doctest](https://docs.python.org/3/library/doctest.html), etc.). This will be done in a future version of the document. + +The current version of this note is organized as follows: +- The first section recalls the objectives of testing in the context of SONNX and give some elements about the test strategy, +- The second section focuses on the practical implementation of the test strategy for SONNX operators +### Test objectives and scope in SONNX + +In SONNX, tests target two main objectives: +- **Validating** the informal specification through comparison with existing implementations (e.g., ONNX runtime) +- **Verifying** implementations of the SONNX specification, using the SONNX reference implementation as the test oracle. + +Concerning the first objective, it could be interpreted at first sight as some sort of "retro-engineering" activity in which the specification is derived from the implementation. *This is clearly not the case*. Tests against an implementation is a way to increase our confidence in the SONNX specification by comparing it to existing reliable implementations. +In case of discrepancy, no immediate conclusion can be drawn since the error may be either in the specification or in the implementation. However, this reveals a problem in either or both sides that needs to be addressed. This approach has already been fruitful by raising issues both in our specification and in the ONNX runtime implementation. + +Concerning the second objective, it is worth noting that passing all tests developed in SONNX does not **guarantee** full conformance with the SONNX specification. In particular, in the context of the development of a certified system, demonstration of conformance remains the responsibility of the applicant. +### Test strategies +#### Functional *vs. implementation-based* tests + +In SONNX, tests are essentially *functional*, i.e., they only refer to *what* the operators are expected to do. They do not consider *how* they will be implemented. + +However, in practice, additional tests may be defined to account for possible implementations solutions and well-known sources of errors. In that case, the reason for the test shall be clearly documented. + +#### Test traceability + +Tests must be traceable to a specific part of the informal specification. Traceability shall be done using reference to the specification section or to a specic requirement tag (e.g., `E_DIV_REAL_FUNC_010`). + +#### Equivalence class-based testing + +Except for very simple cases such as e.g., **Add** on int8 scalars, it is generally impossible test an operator against all its possible input values. Therefore, tests are generally performed on a selected subset of all possible input values. + +One strategy to select those values can be based on the definition of *equivalence classes*. + +Two values $x$ and $y$ are said to belong to the same equivalence class with respect to testing (i.e., $a R b$) if the capability of those values to reveal some design or implementation error are *expected* to be the same, i.e., if an error is revealed by input $a$ then it is also revealed by input $b$. For instance, testing **Add(x1,x2)** with $a=(x_1:10,x_2:20)$ is considered to reveal the same kind of errors as with $b=(x_1:15, x_2:-10)$. + +This definition clearly shows that equivalence classes rely on some *hypotheses* about some "reasonable" underlying fault model. For instance, in the previous example, nothing prevents a specific implementation to behave correctly for $x=10,y=20$ and incorrectly for $x=15,y=-10$: +``` +int add(int x, int y) { + if ((15 == x) && (-10 ==y) + return 42; + else + return x+y; +} +``` +But in general, such fault model -- which is some kind of "easter-egg" in this case -- is simply deemed very unlikely and is not considered in the definition of the equivalence classes. + +Reciprocally, some fault models are well-known. This is, for instance, the case of faults concerning the incorrect handling of domain boundaries, whether it is the bounds of an array (i.e., the min and max value of an index in this array), the min and max values of some data type, some "special" functional values (e.g., a null denominator for a division), etc. + +Since we are considering *functional* testing, classes of equivalence are based on the specified behaviour of the operator, not on any specific implementation. + +#### Equivalence classes based on the input domain + +##### Principles + + In some cases, equivalence classes are derived from the domains of valid values of variables. A domain by be defined by the type of the variable (for instance, $[0,255]$ for an unsigned integer, $[-128,127]$ for a signed integer, etc.), or by some constraint involving one or multiple variables. + +Once the domain is defined, several equivalence classes can be built, for instance: +- the class of all values on the boundary of the domain +- the class of all values inside the domain defined by the boundary (the boundary themselves being excluded). + +Let us consider a domain defined by some predicate $p_i(x_1,x_2,...,x_n)$ over a subset of the operator input variables. A predicate can be an inequality or an equality relation involving some input variables. For instance: $x_1< 10$, $x_1+x_2\leq x_3$ or $x_1^2+x_2^2=c$, etc. A domain is defined by the set of predicates $P={p_1,p_2,...,p_m}$. + +We want to distinguish the points located on the boundary of the domain defined by the set of constraints from the points located in the domain but not on the boundary. + +First, we replace all strict inequality by non-strict inequality. +For instance, in the integer domain, predicate $x_1<10$ become $x_1\le 9$. +In the floating point domain, $x_1+x_2 0$ + - $\text{dilations}[1] > 0$ + - $\text{strides}[0]>0$ + - $\text{strides}[1] > 0$ + - The kernel shall not completely fit in the padding area (\*) + - $\text{dilations}[0]\times(dW_0-1)+1 > \text{pads}[0]$ + - $\text{dilations}[0]\times(dW_0-1)+1 > \text{pads}[2]$ + - $\text{dilations}[1]\times(dW_1-1)+1 > \text{pads}[1]$ + - $\text{dilations}[1]\times(dW_1-1)+1 > \text{pads}[3]$ + - Size of the output tensor (\*\*) + - $(dY_2-1)\times\text{strides}[0]+(\text{dilations}[0] (dW_0-1)+1) \le dX2+\text{pads}[0]+\text{pads}[2]$ + - $dY_2\times\text{strides}[0]+(\text{dilations}[0] (dW_0-1)+1) \gt dX2+\text{pads}[0]+\text{pads}[2]$ + +(\*) This constraint is due to the fact that the operator also returns the index of the maximum value **in the input tensor**. This index does not makes sense if the kernel completely "fits" in the padding area since, in that case, the maximum value is found in the padded area. +In ONNX runtime, the constraint is stronger: the padding shall be smaller than the kernel size (before dilation). +(\*\*) If the size of output tensor is $dY_2$ , it means that the kernel has been applied $dY_2$ times on the input. The application of the kernel shall not overflow the padded tensor, so $$(dY_2-1)\times\text{strides}[0]+(\text{dilations}[0] (dW_0-1)+1) \le dX2+\text{pads}[0]+\text{pads}[2]$$ But the kernel shall be applied as many times as possible, so we have also +$$dY_2\times\text{strides}[0]+(\text{dilations}[0] (dW_0-1)+1) \gt dX2+\text{pads}[0]+\text{pads}[2]$$ + +##### Solving the system of equations + +When considering all constraints, finding all solutions can become extremely tedious. One possible solution is to use a constraint solver such as z3. An example is given in this [Jupyter notebook](https://colab.research.google.com/drive/14wIuDS6uYxWioI7IzVirBsSdbtp0xbct?usp=sharing). + +##### Eliminating useless combinations of constraints + +Up to now, we have considered all possible combinations of constraints without considering the actual dependencies between those predicates and, more generally, between variables. + +The first case is the one where some predicates are incompatible. For instance, when the predicates define the boundaries of an interval (its left and right bounds), they cannot be simultaneous true: a value cannot be simultaneously the minimum and the maximum of the domain, except for intervals reduced to a unique value. Those combinations can be eliminated when building the set of constraints. However, if some solver is used (e.g., Z3), they can be left since they will easily be detected as "non satisfiable" (NONSAT) by the solver. + +The second case concern combinations that are considered not pertinent with respect to the test objectives. By "not pertinent", we mean that *we do not expect a particular erroneous behaviour for that specific combination of values*. + +For instance, let's consider a pointwise operation such as **$Y$=Add($X_1$,$X_2$)**. It is not necessary (and usually impossible) to generate test cases for every possible value of tensors $X_1$ and $X_2$. +It is not necessary either to generate test cases so that for any couple of multi-index $(i, i')$, $X_1[i]$ and $X_1[i']$ takes every possible value in min, max, and some value in-between. Indeed, from a functional perspective, computation of $Y[i]$ does only depend on the input $X_1[i]$. +Similarly, we consider that the behaviour of the operator does not depend on the exact value of the multi-index, so if the operator behaves correctly for some multi-index $i$, then we consider that it will behave correctly for any other multi-indexes $i'$. In practice, this mean that we can test the operator on the domain of the elements of the input tensors for some multi-index $i$ to ensure that the computation on one element is correct. So we will test (e.g.) $X_1[0]+X_2[0]$ for all combinations in $\{\text{minint}, \text{maxint}, x \in ]\text{minint}, \text{maxint}[\}$. + +However, additional tests must be done with respect to the domain of the multi-indexes themselves, but we will not fully test the $X_1[i]+X_2[i]$ for each multi-index $i$ on the boundary of the multi-index domain. Instead, we will +- "fully" test the addition for some multi-index $i$ (e.g., $i=0$ for a unidimensional tensor, or an empty multi-index for a scalar tensor) +- test the addition on one value for each boundary of the multi-index domain. + +Stated differently, test cases are generated separately for each independent subset of variables so that we do not need to consider all combinations of constraints on the values of each element of each tensor of each possible multi-index. + +However, the absence of dependency must be assessed. For instance, if we consider an operator that computes a matrix multiplication, we know that the accumulator creates a dependency between the size of the tensor and the values. Indeed, the greater the tensor and the greater the values contained in the tensor, the greater the value of the accumulator. Since the accumulator may overflow (which is a classical fault model), we have to consider the specific combination of the largest sizes and the largest values! +For instance, the **MatMulInteger** operator will overflow when multiplying two unsigned integer vectors of size $N> {2^{32}-1 \over 255^2}$ if all the values in the tensor are equal to 255. + +In the example of the **Maxpool** operator, we can consider the set of constraints concerning the first spatial dimension and the second spatial dimension separately, wihci + +##### Unbounded variables + +All variables are bounded by their types but for some of them such as variables describing a structural property such as a rank or a dimension, the bounds defined by the type can not be tested. + +For instance the right bound of the tensor ranks and sizes domains can be -- theoretically -- as large as $2^{32}-1$ , but, in reality, those bounds are impossible to reach due to physical limits on the memory and/or execution time. In that case, tests shall be carried out against some arbitrary "limit" (for instance, a rank of 4 and a size of 1000 per dimension...). Those arbitrary limits can be handled in exactly the same way as those reflecting the actual domain of the operator even though they do not reflect any functional limit. They can be considered as "usage domain" conditions, i.e., conditions reflecting the domain in which the operator are guaranteed to behave correctly. + +##### Symmetry and asymmetry + +Consider the the `pads` parameter for the convolution. Besides testing the boundaries of the domain (in 2D), we may also exercice certain relations between the parameters. For instance, we may want to exercize symmetric and asymmetric padding. It may be the case that by generating tests with respect to the bounds of the domain will also cover symmetric and asymmetric configurations (simply because we will generate a test for all combinations of min and max values for all paddings), but this is fortuitous. A good test strategy shall make this test against symmetry explicit. + +##### Test completeness + +In our context, a test set is deemed complete relative to an operator specification if : +1. It covers every equivalence classes built upon data-types, for all data types supported by the operator +2. It covers every equivalence classes built upon the tensor shapes (rank and sizes) +3. It covers every equivalence classes built upon broadcasting +4. It covers every equivalence classes built upon the operator pre-conditions (so-called "constraints" in the specification) +5. It covers every equivalence classes built upon the operator functional specification +6. (it covers every important implementation-risk pattern.) + +### Domains +#### Type-specific domains +Type-specific domains are defined with respect to the data type. They are independent of the semantics of operators. +##### Floating point numbers +For instance, for floating point number, the domains are the following (some domains are singletons): +- NaN +- +inf, -inf +- +0, -0 +- subnormal values (also called "denormalized" values) + - for float: + - min positive: $2^{-149} \approx 1.45\times 10^{-45}$ + - max positive: $(1−2^{−23})\times 2^{−126} \approx 1.18×10^{-38}$ + - for double: + - min positive: $2^{-1074} \approx 4.9×10^{-324}$ + - max positive: $(1−2^{−52})\times2^{−1022} \approx 2.23\times 10^{-308}$ +- normal value + - for float: + - min positive: $2^{-126} \approx 1.18×10^{-38}$ + - max positive: $(1-2^{-24})\times 2^{128} \approx 3.40 \times 10^{38}$ + - for double + - min positive: $2^{-1022} \approx 2.23\times 10^{-308}$ + - max positive: $(1-2^{-53})\times 2^{1024} \approx 1.80 \times 10^{308}$ + +Note: Several reasons make subnormal values worth considering: +- This is "where" underflows may occur (values rounded to 0) +- The relative precision of subnormal values is lower than for normal number. Indeed, normal numbers have *fixed relative* precision whereas subnormal numbers have *fixed absolute* spacing near zero. Therefore, their relative precision becomes worse as they get closer to zero. (However, note that accuracy is not addressed in the specification) +- The treatment of subnormal values may depend on the compiler / hardware platform +- The subnormal hey may expose corner cases in comparisons and branching (e.g., comparison to 0) + +The domain is pretty complex, so a strict application of the test generation strategy will end-up with a very large number of test cases, even for an operator with two arguments. A strategy must be defined to restrict the number of combinations to be considered. + +##### Integer numbers +For integer numbers (uint, int): +- min int (``minInt`` for the considered integer type) +- max int (``maxInt`` for the considered integer type) +#### Structure-specific domains + +In the context of SONNX, a structure is a tensor characterized by its shape (number of dimensions -- or rank -- and a size per dimension). The domains are defined with respect to these two parameters. + +In the context of SONNX, tests shall consider the +- rank + - scalar tensors (rank = 0) + - vector (rank=1) + - matrix (rank=2) + - all other ranks +- dimension + - null-tensors (at least one dimension with size zero) + * multi-dimensional tensors reduced to a lower dimension tensor, such as a 2-dimensional tensor of shapes 1x1 (a scalar), 1xn (a line vector), nx1 (a column vector) + * all other dimensions +#### Operator-specific constraints + +Operator-specific constraints are derived from the operator semantics, that is to say from the informal (or formal) specification. They do not concern pre-conditions (which have been covered in the previous sections). + +We consider two general cases: +- the case of "special values", which consist in identifying of values playing a "special" role in the specification of the operator +- the case of "properties", which consists in identifying of special properties of the operator. +##### Special values + +The test designer has to consider the existence of: +- neutral values +- absorbing values +- dominating values + +For instance: +- for **Add** + - 0 : neutral element +- for **Mul** + - 1 : neutral element + - 0 : absorbing element +- for **Div** + - 1 at denominator : neutral element + +- for **Max** + - +inf : absorbing value + - -inf : neutral element +- etc. + +Tests must be generated for all these values. + +###### Example + +For the **Maxpool** operator: +- `-inf` is a neutral element +- `+inf` is an absorbing element + +##### Discontinuities + +Values representing a "discontinuity" in the function behavior (in the general and mathematical sense), including exceptional cases and error cases. + +- for **Div** + - 0 at denominator : discontinuity (division-by-zero) +- for **Relu** + - 0 : Relu threshold +##### High-level functional properties +The test designer has to consider some generic properties of the operator such as : +- commutativity (**Add**, **Mul**, **Max**, etc.), +- linearity (e.g., **Conv**(X+Y,W) = **Conv**(X,W)+**Conv**(Y,W)). +- invariance against a transformation (e.g., **Sin**(x)=**Sin**(x+2k\Pi), the indices returned by **MaxPool(** are invariant by the addition of a constant $c$ to all elements of the tensor). + +Tests must be generated to verify that these relations hold. + +###### Example +*To be completed.* + + +#### Testing against functional properties + +Testing against the input domain was a matter of covering equivalence classes derived from the domain of definition of the operator. The domain of definition is defined considering type constraints and preconditions. +Basically, we have considered two equivalence classes: +- $Cb$: class of all inputs on the boundary of the domain of definition +- $Ci$: class of all inputs in the domain of definition, without the boundaries. + +Testing against the functional properties consists in partitioning $Cb \cup C_i$ the same domain considering the definition of the behaviour of the operator. + +For instance, the **Abs** operator is defined as follows for the floating point numbers, where $i$ is any multi-index compatible with the structure of $X$: +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i] = \text{NaN} \\ +\text{+Inf} & \text{if } X[i] = \pm \text{Inf} \\ +\text{+0} & \text{if } X[i] = \pm \text{0} \\ +-X[i] & \text{if } X[i] \lt 0  \\ +X[i] & \text{otherwise} +\end{cases} +$$ +So, there must be a test for each of the classes defined by the conditions: +- $p'1 : (X[i] = NaN)$ +- $p'2 : (X[i] = +\text{Inf})$ +- $p'3 : (X[i] = -\text{Inf})$ +- $p'4 : (X[i] = -\text{0})$ +- $p'5 : (X[i] = +\text{0})$ +- $p'6 : (X[i] < 0) \wedge (X[i] \neq \text{-Inf})$ +- $p'7 : (X[i] > 0) \wedge (X[i] \neq \text{+Inf})$ + +This operator applies on a signal argument $X$ characterized by +- a number $n$ of dimensions (or rank) +- a size for each dimension +- a value of each element $X[i]$, with $i$ a multi-index compatible with the rank and sizes + +The complete domain is the cartesian product $\mathbb{F}^{dX_0+dX_1+ ... +dX_n}$ + +As shown on the functional specification, the behaviour of the operator does not depend on the value of the muti-index $i$, so there is no need to exercice all combinations of $i$ (rank and sizes) and values. +If for any given $i$, the operator behaves correctly for all of the values defined previously, then it is considered to behave correctly for all of the values and all $i$. + +### On values and indexes... + +When designing the test, care shall be taken to verify that the result of the computation of some input value $X[i]$ for multi-index $i$ is actually stored at the appropriate output $Y[i']$. more generally, we are checking that $Y[i'_1]=f_1(X[i_1], X[i_2], ..., X[i_n]$, $Y[i'_2]=f_2(X[i_1], X[i_2], ..., X[i_n]$, etc.) which means that we are testing that the values are correct in the correct for the correct multi-indexes. + +As the indexes $i'$ are not explicit outputs of the operator, the test must be designed so that the value of $i'$ can be derived from the observation of the output tensor $Y$. For instance, when testing the **Mul($X1$,$X2$)** operator, the contents of $X1$ and $X2$ must be such that all expected values of $Y$ are different. Using $X2[i]=0$ for all $i$ will, for instance, not satisfy this property since all $Y[i']$ will be equal to zero so there will be no way to check that the 0 at $Y[i']$ have been computed from the appopriate $X1[i]$ and $X2[i]$ (in that case, $i$ = $i'$). + +The same constraint applies when dealing with structural operators such as **Flatten**. Note that the explicit property stating that the **Flatten** operator must preserve the values of the input tensor (invariance) is not sufficient since the output may well preserve the input values but place them at inappropriate indexes. diff --git a/safety-related-profile/sonnx/ops/docs/installation/why3_installation.md b/safety-related-profile/sonnx/ops/docs/installation/why3_installation.md new file mode 100644 index 00000000..5dbef3ef --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/installation/why3_installation.md @@ -0,0 +1,104 @@ + +# Installation + +## Why3Find Installation Guide + +This guide sets up a full OCaml + Why3 + Alt-Ergo + `why3find` environment on **Ubuntu/Debian-based systems**. + +--- + +## 🛠️ 1. Install System Dependencies + +```bash +sudo apt-get update +sudo apt-get install -y \ + build-essential \ + curl \ + git \ + m4 \ + unzip \ + bubblewrap \ + pkg-config \ + libgmp-dev \ + libzmq3-dev \ + zlib1g-dev \ + libexpat1-dev \ + libgtk-3-dev \ + libgtksourceview-3.0-dev +``` + +--- + +## 🐫 2. Install OPAM & Initialize OCaml Environment + +```bash +sudo apt-get install -y opam + +opam init --disable-sandboxing -y +eval $(opam env) + +opam switch create 4.13.0 +eval $(opam env) +``` + +--- + +## 📦 3. Install OCaml Packages + +```bash +opam install -y \ + "dune>=3.12" \ + "dune-site>=3.12" \ + "why3=1.8.0" \ + "yojson>=1.7.0" \ + "zmq>=5.0.0" \ + "terminal_size>=0.2.0" +``` + +--- + +## 🔍 4. Install `why3find` + +### Option A: From OPAM + +```bash +opam install why3find +``` + +### Option B: From Git Repository + +```bash +git clone https://git.frama-c.com/pub/why3find.git +cd why3find +opam install . +``` + +--- + +## 🧪 5. Install Alt-Ergo with Tests + +```bash +opam install -y alt-ergo=2.4.2 --with-test +``` + +--- + +## 📚 6. Install `odoc` with Documentation + +```bash +opam install -y odoc --with-doc +``` + +--- + +## 🧹 7. Install VSCode Extension for Why3 Platform + +```bash +wget https://git.frama-c.com/pub/why3find/-/jobs/1177337/artifacts/raw/vscode/why3-platform-1.1.1.vsix +code --install-extension why3-platform-1.1.1.vsix +``` + + + + + diff --git a/safety-related-profile/sonnx/ops/docs/tensor_lib/imgs/diagram.png b/safety-related-profile/sonnx/ops/docs/tensor_lib/imgs/diagram.png new file mode 100644 index 00000000..d2b2d284 Binary files /dev/null and b/safety-related-profile/sonnx/ops/docs/tensor_lib/imgs/diagram.png differ diff --git a/safety-related-profile/sonnx/ops/docs/tensor_lib/tensor.md b/safety-related-profile/sonnx/ops/docs/tensor_lib/tensor.md new file mode 100644 index 00000000..f1987c08 --- /dev/null +++ b/safety-related-profile/sonnx/ops/docs/tensor_lib/tensor.md @@ -0,0 +1,148 @@ +# Formally Verified C Tensor Library + +This project is a provably correct tensor library written in C, with its correctness formally verified using **Why3** and its **WhyML language**. +The primary goal is to demonstrate how formal verification can be applied to ensure that low-level C code for numerical operations behaves exactly as intended by a high-level mathematical model. + +--- + +## Project Structure + +These files provide a **formal verification system** for a tensor library, primarily using the **WhyML language** with **Why3**. +The goal is to **prove the correctness of C-like tensor operations**. +This repository contains: + - Formal specification files (`.mlw`) + - The C driver file (`.drv`) + - A `Makefile` to automate the process + +### File Breakdown + +#### `tensor.mlw` +This file is the **core of the formalization**, defining the fundamental mathematical concepts and data structures for tensors. +It serves as a **foundational library** for other modules. + +- **Range module** + - `size`: Calculates the total number of elements in a tensor (recursive multiplication of dimensions). + - `positive`: Predicate that checks all dimensions are positive integers. + - `valid`: Verifies coordinates are within the valid range for given dimensions. + +- **Tensor module** + - `data`: Represents tensor elements as a map from coordinates to values. + - `tensor`: Record with dimensions (`dims`), data (`data`), and background value. + - **Predicates**: `~`, `~=`, `==` define equality and similarity notions for tensors. + - **Ghost Functions**: they are for verification only and won't be compiled to C + - `scalar`: Single-valued tensor + - `zero`: Tensor of all zeros + - `const`: Tensor with a constant value + +--- + +#### `layout.mlw` +This file (module **CFlat**) focuses on the **memory layout of tensors**. + +- **Functions** + - `offset`: Maps multidimensional coordinates to a flat memory address (generalized row-major offset). + - `index`: Inverse of `offset`, mapping an address back to coordinates. + +- **Lemmas** + - `index_of_offset`: Proves correctness and reversibility of `offset` and `index`. + +--- + +#### `libvector.mlw` +This module bridges **abstract concepts** with **concrete C arrays**. + +- **Types and Functions** + - `iarray`: Pointer to a C array of 32-bit integers. + - `ivector`, `islice`: Convert `iarray` to WhyML lists. + - `cdim_size`: C implementation of total size calculation, proven equivalent to the abstract definition. + - `coffset`: C implementation of memory offset, proven equivalent to `layout.mlw`’s `offset`. + +--- + +#### `libtensor.mlw` +Provides a **C-like representation of tensors** and proofs linking them to abstract models. + +- **Types** + - `ctensor`: Concrete C representation (rank, dimensions array, data array). + +- **Ghost Functions** + - `tensor`, `tensorb`: Convert `ctensor` into abstract `tensor`. + +- **C Functions (with proofs)** + - `ctensor_clear`: Fills tensor with zeros → proven equivalent to `Tensor.zero`. + - `ctensor_reset`: Fills tensor with constant → proven equivalent to `Tensor.const`. + - `ctensor_where`: Conditional element assignment → proven equivalent to `opwhere`. + +--- + +#### `cdriver.drv` +A **Why3 driver file** that specifies mappings from WhyML to C. + +- **std.Clib**: Maps standard C library functions (`stdlib.h`, `stdint.h`). +- **std.Cfloat**: Maps floating-point operations (`+`, `-`, `*`, `/`) to C equivalents. +- **Other modules**: Headers like `cindex.h` for extracted code. + +--- + +#### `Makefile` +Automates the **build and verification process**. + +- `prove`: Runs Why3 proofs on all `.mlw` files. +- `doc`: Generates HTML documentation. +- `lib`: Extracts verified WhyML to C and compiles into a library. +- `clean`: Removes generated files. + +--- +### Dependency Diagram + +![Project Dependency Graph](imgs/diagram.png) + +--- +## Making the tensor library visible + +To expose the tensor library, we need to add its path to the .why3.conf file (typically located in your home directory), for example: +```code +loadpath ="/home/user/sonnx/working-groups/safety-related-profile/sonnx/ops/spec/formal/common/libs" +``` +You can adapt this path according to your local setup. + +**Note** + +When using tensor modules inside other Why3 modules, their usage must be prefixed with 'tensor', for example: +```code +use tensor.std.Int +use tensor.std.List +use tensor.std.Clib +use tensor.std.Cfloat +``` + +For more details refer to the formal specification of Where operator. +## Building and Verification + +The `Makefile` simplifies the entire process with a few commands: + +### Prove all properties +```bash +make prove +``` +This command runs the Why3 proof engine on all the .mlw files to check that all logical properties and correctness conditions hold. +### Generate the C library: +```bash +make lib +``` +This command first extracts the verified WhyML code into C source files and then compiles them into a library. + +### Generate documentation +```bash +make doc +``` +This command creates an HTML documentation from the formal specifications in the WhyML files. + +### Clean the project +```bash +make clean +``` +Removes all generated files, including the C library and documentation. and insert the figure in the right place + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/abs/abs.mlw b/safety-related-profile/sonnx/ops/spec/formal/abs/abs.mlw new file mode 100644 index 00000000..ca499186 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/abs/abs.mlw @@ -0,0 +1,18 @@ +(** + Specification of Abs operation on tensors. + *) + +module Abs + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + + let function abs (a : tensor 'a) : tensor 'a = + { + shape = a.shape ; + value = fun i -> if a.value[i] < 0 then -a.value[i] else a.value[i] ; + } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/add/Makefile b/safety-related-profile/sonnx/ops/spec/formal/add/Makefile new file mode 100644 index 00000000..bbada3fa --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/add/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'add' +# Ce Makefile est exécuté depuis : ops/spec/formal/add/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/add/generated_doc +CODE_DIR = ../../../code/add/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'add' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'add'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Add operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_ADD = -D c -D add_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_ADD) add.CTensorAdd + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/add/add.mlw b/safety-related-profile/sonnx/ops/spec/formal/add/add.mlw new file mode 100644 index 00000000..573e771a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/add/add.mlw @@ -0,0 +1,62 @@ +module OPAdd + use tensor.tensor.Tensor + use int.Int + use real.Real + use tensor.std.Cfloat + use tensor.tensor.Range + + let ghost function dadd (a b : tensor real) : data real + = fun ks -> + if valid ks a.dims then + a.data ks + b.data ks + else + a.background + + + let ghost function opadd (a b : tensor real) : tensor real + requires { a ~= b } + ensures { result ~= a } + ensures { result.data = dadd a b } + = { dims = a.dims ; + data = dadd a b ; + background = a.background } + +end + +module CTensorAdd + use int.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use ref.Ref + use tensor.OPAdd + use tensor.libtensor.CTensor + +let ctensor_add (a b r : ctensor) = + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + ensures { tensor r = opadd (tensor a) (tensor b) } + (*proof*) + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + invariant { forall k. + 0 <= k < i -> + value_at r.t_data k = + value_at a.t_data k .+ value_at b.t_data k + } + r.t_data[i] <- a.t_data[i] .+ b.t_data[i] + done + ; + assert { tensor r == opadd (tensor a) (tensor b) } + (*qed*) + + + end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/add/add_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/add/add_driver.drv new file mode 100644 index 00000000..cacdad62 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/add/add_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module add.CTensorAdd + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/argmax/Makefile b/safety-related-profile/sonnx/ops/spec/formal/argmax/Makefile new file mode 100644 index 00000000..5d86de04 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/argmax/Makefile @@ -0,0 +1,94 @@ +.RECIPEPREFIX := > + +# ========================================================== +# Makefile pour ArgMax +# - preuve de la spécification abstraite +# - extraction C du kernel concret CtensorArgMax +# ========================================================== + +.PHONY: prove prove_all update doc lib show grep clean veryclean spec_only + +DOC_DIR = ./argmax_generated/doc +CODE_DIR = ./argmax_generated/c_code + +CDRIVER_DIR = $(HOME)/working-groups/safety-related-profile/sonnx/ops/spec/formal/common/drivers +LIBTENSOR_DRIVER_DIR = $(HOME)/working-groups/safety-related-profile/sonnx/ops/spec/formal/common/drivers +SRC_TENSOR = $(HOME)/working-groups/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor + +SRC = argmax.mlw +SPEC_ONLY = argmax_spec_only.mlw + +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_ARGMAX = -D c -D argmax_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) + +spec_only: +> @echo "------------------------------------------" +> @echo "--- Building abstract-only ArgMax file" +> @echo "------------------------------------------" +> @python3 -c 'from pathlib import Path; s=Path("argmax.mlw").read_text(); marker="\nmodule ArgMax_Concrete"; assert marker in s, "module ArgMax_Concrete not found"; Path("argmax_spec_only.mlw").write_text(s.split(marker)[0].rstrip()+"\n"); print("OK: generated argmax_spec_only.mlw")' + +prove: spec_only +> @echo "------------------------------------------" +> @echo "--- Proofs for abstract ArgMax specification" +> @echo "------------------------------------------" +> @why3find prove $(SPEC_ONLY) -l -s -x + +prove_all: +> @echo "------------------------------------------" +> @echo "--- Proofs for all ArgMax theories" +> @echo "------------------------------------------" +> @why3find prove $(SRC) -l -s -x + +update: spec_only +> @echo "------------------------------------------" +> @echo "--- Update Proofs for abstract ArgMax" +> @echo "------------------------------------------" +> @why3find prove $(SPEC_ONLY) -l -s -x -m + +doc: spec_only +> @echo "------------------------------------------" +> @echo "--- Documentation" +> @echo "------------------------------------------" +> @rm -rf $(DOC_DIR) +> @mkdir -p $(DOC_DIR) +> @why3find doc $(SPEC_ONLY) -t "ArgMax operator" -o $(DOC_DIR) + +lib: +> @echo "------------------------------------------" +> @echo "--- Extraction" +> @echo "------------------------------------------" +> @rm -rf $(CODE_DIR) +> @mkdir -p $(CODE_DIR) +> @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor +> @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex +> @why3 extract $(EXTRACT_ARGMAX) argmax.ArgMax_Concrete +> @python3 patch_argmax_c.py +> @echo "------------------------------------------" +> @echo "--- C compilation" +> @echo "------------------------------------------" +> @cd $(CODE_DIR) && gcc -c *.c -lm +> @echo "------------------------------------------" +> @echo "--- Generated files" +> @echo "------------------------------------------" +> @find $(CODE_DIR) -type f | sort + +show: +> @echo "------------------------------------------" +> @echo "--- Generated files" +> @echo "------------------------------------------" +> @find $(CODE_DIR) -type f | sort + +grep: +> @echo "------------------------------------------" +> @echo "--- Search ArgMax symbols" +> @echo "------------------------------------------" +> @grep -R "c_argmax\\|argmax\\|isnan\\|isgreater" -n $(CODE_DIR) || true + +clean: +> @rm -rf $(CODE_DIR) +> @rm -rf $(DOC_DIR) +> @rm -f $(SPEC_ONLY) + +veryclean: +> @rm -rf argmax_generated html +> @rm -f $(SPEC_ONLY) diff --git a/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax.mlw b/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax.mlw new file mode 100644 index 00000000..04ee9d9f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax.mlw @@ -0,0 +1,268 @@ +(* + ============================================================ + ArgMax - Formal specification + ============================================================ + + Based on the informal specification: + - axis must be set + - keepdims must be set + - select_last_index must be set + - axis >= 0 + - axis < rank(X) + - dX_axis > 0 + - keepdims in {0,1} + - select_last_index in {0,1} + - Y contains int64 indices + - 0 <= Y[k] < dX_axis + - if select_last_index = 0, first maximum index is selected + - if select_last_index = 1, last maximum index is selected + - for floating values, NaN has priority over finite and infinite values +*) + +module ArgMax_Abstract + + use int.Int + use array.Array + + (* + Abstract value type. + + This module is intentionally generic: + - for real/integer types: is_nan(v) is always false. + - for floating types: is_nan(v) models IEEE NaN. + *) + type value + + predicate is_nan value + + predicate gt value value + predicate eqv value value + + axiom eqv_refl: + forall x:value. eqv x x + + (* + gt represents the strict ordering used when there is no NaN. + For integers/reals, this is the usual >. + For floats, this is the usual floating-point order on non-NaN values: + +inf > finite > -inf. + *) + + predicate valid_attrs (axis:int) (keepdims:int) (select_last_index:int) (rank:int) = + axis >= 0 /\ + axis < rank /\ + (keepdims = 0 \/ keepdims = 1) /\ + (select_last_index = 0 \/ select_last_index = 1) + + predicate valid_reduced_dim (d_axis:int) = + d_axis > 0 + + (* + S(k,j) is modeled as a function slice_value. + + For a fixed output logical position k, slice_value k j + denotes the value of X at the corresponding coordinates, with j inserted + along the reduced axis. + *) + type out_pos + + function slice_value out_pos int : value + + (* + NaN priority for floating-point ArgMax. + A slice has a NaN if at least one index j along the reduced axis + contains NaN. + *) + predicate slice_has_nan (k:out_pos) (d_axis:int) = + exists j:int. 0 <= j < d_axis /\ is_nan (slice_value k j) + + predicate first_nan_index (k:out_pos) (d_axis:int) (j:int) = + 0 <= j < d_axis /\ + is_nan (slice_value k j) /\ + forall p:int. 0 <= p < j -> not (is_nan (slice_value k p)) + + predicate last_nan_index (k:out_pos) (d_axis:int) (j:int) = + 0 <= j < d_axis /\ + is_nan (slice_value k j) /\ + forall p:int. j < p < d_axis -> not (is_nan (slice_value k p)) + + (* + Maximum definition when the slice contains no NaN. + *) + predicate is_maximum_index (k:out_pos) (d_axis:int) (j:int) = + 0 <= j < d_axis /\ + forall p:int. + 0 <= p < d_axis -> + not (gt (slice_value k p) (slice_value k j)) + + predicate first_maximum_index (k:out_pos) (d_axis:int) (j:int) = + is_maximum_index k d_axis j /\ + forall p:int. + 0 <= p < j -> + not (eqv (slice_value k p) (slice_value k j)) + + predicate last_maximum_index (k:out_pos) (d_axis:int) (j:int) = + is_maximum_index k d_axis j /\ + forall p:int. + j < p < d_axis -> + not (eqv (slice_value k p) (slice_value k j)) + + (* + Complete abstract ArgMax result predicate. + + This is the heart of the formal specification. + *) + predicate argmax_result + (k:out_pos) + (d_axis:int) + (select_last_index:int) + (y:int) + = + valid_reduced_dim d_axis /\ + 0 <= y < d_axis /\ + ( + ( + slice_has_nan k d_axis /\ + ( + (select_last_index = 0 /\ first_nan_index k d_axis y) \/ + (select_last_index = 1 /\ last_nan_index k d_axis y) + ) + ) + \/ + ( + not (slice_has_nan k d_axis) /\ + ( + (select_last_index = 0 /\ first_maximum_index k d_axis y) \/ + (select_last_index = 1 /\ last_maximum_index k d_axis y) + ) + ) + ) + + (* + Shape rule. + + If keepdims = 1: + rank(Y) = rank(X) + dimY[axis] = 1 + all other dimensions are unchanged. + + If keepdims = 0: + rank(Y) = rank(X) - 1 + axis dimension is removed. + *) + predicate output_shape_keepdims + (rank:int) + (axis:int) + (dx:array int) + (dy:array int) + = + dy.length = rank /\ + forall i:int. + 0 <= i < rank -> + ( + (i = axis /\ dy[i] = 1) \/ + (i <> axis /\ dy[i] = dx[i]) + ) + + predicate output_shape_nokeepdims + (rank:int) + (axis:int) + (dx:array int) + (dy:array int) + = + dy.length = rank - 1 /\ + forall i:int. + 0 <= i < rank - 1 -> + ( + (i < axis /\ dy[i] = dx[i]) \/ + (i >= axis /\ dy[i] = dx[i + 1]) + ) + + predicate output_shape + (rank:int) + (axis:int) + (keepdims:int) + (dx:array int) + (dy:array int) + = + (keepdims = 1 /\ output_shape_keepdims rank axis dx dy) \/ + (keepdims = 0 /\ output_shape_nokeepdims rank axis dx dy) + +end + +module ArgMax_Concrete + + use int.Int + use array.Array + use ref.Ref + + type value + + predicate is_nan value + predicate gt value value + predicate eqv value value + + predicate better_first (candidate:value) (current:value) = + (is_nan candidate /\ not (is_nan current)) \/ + (not (is_nan candidate) /\ not (is_nan current) /\ gt candidate current) + + predicate better_last (candidate:value) (current:value) = + (is_nan candidate /\ not (is_nan current)) \/ + (is_nan candidate /\ is_nan current) \/ + (not (is_nan candidate) /\ not (is_nan current) /\ gt candidate current) \/ + (not (is_nan candidate) /\ not (is_nan current) /\ eqv candidate current) + + (* + Version concrète 1D utilisée pour l'extraction C. + + Elle spécifie le coeur d'ArgMax sur une tranche plate : + - x contient la tranche à réduire ; + - n est la taille de la tranche ; + - select_last_index choisit la stratégie en cas d'égalité ; + - le résultat est toujours un index dans [0, n). + *) + let c_argmax_1d + (x:array value) + (n:int) + (select_last_index:int) + : int + requires { n > 0 } + requires { x.length >= n } + requires { select_last_index = 0 \/ select_last_index = 1 } + ensures { 0 <= result < n } + = + let selected = ref 0 in + let current = ref x[0] in + let j = ref 1 in + + while !j < n do + invariant { 1 <= !j <= n } + invariant { 0 <= !selected < n } + variant { n - !j } + + let v = x[!j] in + + if select_last_index = 0 then + if better_first v !current then + begin + current := v; + selected := !j + end + else + () + else + if better_last v !current then + begin + current := v; + selected := !j + end + else + (); + + j := !j + 1 + done; + + !selected + +end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax_driver.drv new file mode 100644 index 00000000..62fc39b4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/argmax/argmax_driver.drv @@ -0,0 +1,41 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + interface export "#include " + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 == %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module argmax.ArgMax_Concrete + interface "#include " + syntax type value "double" + syntax type int "int64_t" + interface "#include " + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + + syntax val c_is_nan "isnan(%1)" + syntax val c_gt "%1 > %2" prec 11 11 10 + syntax val c_eq "%1 == %2" prec 11 11 10 +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/argmax/why3find.json b/safety-related-profile/sonnx/ops/spec/formal/argmax/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/argmax/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/Makefile b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/Makefile new file mode 100644 index 00000000..d49b34d0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'BatchNorm' +# Ce Makefile est exécuté depuis : ops/spec/formal/batch_normalization/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/batch_normalization/generated_doc +CODE_DIR = ../../../code/batch_normalization/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'BatchNorm' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'BatchNorm'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "BatchNorm operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_BATCHNORM = -D c -D batchnorm_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_BATCHNORM) batch_normalization.CTensorBatchNorm + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization.mlw b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization.mlw new file mode 100644 index 00000000..e9be389d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization.mlw @@ -0,0 +1,182 @@ +(** Formal specification of the BatchNormalization operator (inference mode) *) + +(** ----------------------------------------------------------------------- + Abstract-level specification (tensor.mlw) + ----------------------------------------------------------------------- *) + +module OPBatchNorm + use tensor.tensor.Tensor + use tensor.tensor.Range + use int.Int + use real.Real + use real.Square + use tensor.std.Cfloat + use list.List + + (** Per-channel normalization function. + For a given channel c, value v, mean m, variance s, scale g and bias b: + batchnorm_elt v m s g b epsilon = g * (v - m) / sqrt(s + epsilon) + b *) + function batchnorm_elt (v : real) (m : real) (s : real) (g : real) (b : real) (eps : real) : real + = + g * (v - m) / sqrt (s + eps) + b + + (** Data-level operator: applies batchnorm_elt element-wise. + [chan] extracts the channel index (axis 1) from a multi-index k. + For a tensor of shape (N, C, d2, ...), the channel index is the + second component of k (index 1). *) + function chan (k : list int) : int = + match k with + | Cons _ (Cons c _) -> c + | Cons c _ -> c (* 1-D case: C is implicitly 1, c = 0 *) + | Nil -> 0 + end + + let ghost function dbatchnorm + (x mean var scale bias : data real) + (eps : real) + : data real + = fun k -> + batchnorm_elt + (x k) + (mean (Cons (chan k) Nil)) + (var (Cons (chan k) Nil)) + (scale (Cons (chan k) Nil)) + (bias (Cons (chan k) Nil)) + eps + + (** Tensor-level operator. + Requires: + - [x] and [y] have the same shape and background + - [mean], [var], [scale], [bias] are 1-D tensors of size C + - [epsilon] > 0 + - [var] is non-negative channel-wise *) + let ghost function opbatchnorm + (x : tensor real) + (scale mean var bias : tensor real) + (eps : real) + : tensor real + requires { Real.(eps > 0.0) } + requires { forall c. valid (Cons c Nil) var.dims -> + Real.(var.data (Cons c Nil) >= 0.0) } + ensures { result ~ x } + ensures { result.background = x.background } + ensures { result.data = + dbatchnorm x.data mean.data var.data scale.data bias.data eps } + (*proof*) + = { dims = x.dims ; + data = dbatchnorm x.data mean.data var.data scale.data bias.data eps ; + background = x.background } + (*qed*) + +end + + +(** ----------------------------------------------------------------------- + Concrete-level implementation (libtensor.mlw) + ----------------------------------------------------------------------- *) + +module CTensorBatchNorm + use int.Int + use real.Real + use real.Square + use tensor.std.List + use tensor.std.Clib + use tensor.std.Int + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use OPBatchNorm + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + use ref.Ref + + (** [csqrt f] computes sqrt(f) for a non-negative real value f. *) + val function csqrt (v : float) : float + requires { Real.(v >= 0.0) } + ensures { Real.(result = sqrt v) } + + (** [ctensor_batchnorm x scale input_mean input_var bias epsilon r] + implements the BatchNormalization operator in inference mode. + + Preconditions + ------------- + - [x], [scale], [input_mean], [input_var], [bias], [r] are valid tensors + - [x] and [r] have the same shape and background (shape consistency, C1) + - [scale], [input_mean], [input_var], [bias] are 1-D tensors of size C, + where C is the number of channels of [x] (shape consistency, C1 on params) + - [scale], [input_mean], [input_var], [bias] have the same type (C2) + - [epsilon] > 0 (attribute constraint C1) + - [input_var[c] >= 0] for all c (non-negativity, C3 on input_var) + + Postcondition + ------------- + - [tensor r = opbatchnorm (tensor x) (tensor scale) (tensor input_mean) + (tensor input_var) (tensor bias) epsilon] *) + let ctensor_batchnorm + (x scale input_mean input_var bias : ctensor) + (eps : float) + (r : ctensor) + (*proof*) + requires { valid_tensor x } + requires { valid_tensor scale } + requires { valid_tensor input_mean } + requires { valid_tensor input_var } + requires { valid_tensor bias } + requires { valid_tensor r } + (* Shape consistency: x and r have the same shape *) + requires { tensor x ~= tensor r } + (* Shape consistency: parameter tensors are 1-D of size C *) + requires { tensor scale ~ tensor input_mean } + requires { tensor input_mean ~ tensor input_var } + requires { tensor input_var ~ tensor bias } + requires { scale.t_rank = 1 } + (* Attribute constraint: epsilon strictly positive *) + requires { Real.(eps > 0.0) } + (* Non-negativity of variance channel-wise *) + requires { forall c. 0 <= c < tensor_size input_var -> + Real.(value_at input_var.t_data c >= 0.0) } + (*qed*) + ensures { tensor r = + opbatchnorm (tensor x) (tensor scale) (tensor input_mean) + (tensor input_var) (tensor bias) eps } + = + let n_size = x.t_dims[ 0] in (* N: batch size *) + let c_size = scale.t_dims[ 0] in (* C: channel count *) + let spatial = cdim_size r.t_dims r.t_rank in (* N*C*d2*.. *) + let nc = n_size * c_size in (* N * C *) + let stride = spatial / nc in (* spatial stride *) + let ref flat = 0 in (* flat index into data *) + for n = 0 to n_size - 1 do + (*proof*) + invariant { forall k. 0 <= k < flat -> + value_at r.t_data k = + let idx = index ( k) (tensor_dim x) in + let c = chan idx in + let v = value_at x.t_data ( k) in + let mu = value_at input_mean.t_data c in + let sigma = value_at input_var.t_data c in + let g = value_at scale.t_data c in + let b = value_at bias.t_data c in + batchnorm_elt v mu sigma g b eps + } + (*qed*) + for c = 0 to c_size - 1 do + let mu = input_mean.t_data[c] in + let sigma = input_var.t_data[c] in + let g = scale.t_data[c] in + let b = bias.t_data[c] in + for s = 0 to stride - 1 do + let v = x.t_data[flat] in + r.t_data[flat] <- g .* ((v .- mu) ./ csqrt (sigma .+ eps)) .+ b ; + flat <- flat + 1 + done + done + done + (*proof*) + ; assert { tensor r == opbatchnorm (tensor x) (tensor scale) (tensor input_mean) + (tensor input_var) (tensor bias) eps } + (*qed*) + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/proof.json b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/proof.json new file mode 100644 index 00000000..e4afa777 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/proof.json @@ -0,0 +1,90 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 34, "time": 0.538 }, + { "prover": "cvc5@1.2.1", "size": 50, "time": 0.537 }, + { "prover": "alt-ergo@2.5.4", "size": 17, "time": 0.525 } + ], + "proofs": { + "CTensorBatchNorm": { + "ctensor_batchnorm": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.041 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + null, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.128 }, + { "prover": "alt-ergo@2.5.4", "time": 0.021 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "cvc5@1.2.1", "time": 0.226 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.178 }, + { "prover": "alt-ergo@2.5.4", "time": 1.0 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.189 }, + { "prover": "alt-ergo@2.5.4", "time": 1.0 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.183 }, + { "prover": "alt-ergo@2.5.4", "time": 0.985 } + ] + }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.18 }, + { "prover": "alt-ergo@2.5.4", "time": 0.989 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.106 }, + null, + { + "tactic": "split_vc", + "children": [ { "prover": "alt-ergo@2.5.4", "time": 1.2 } ] + }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.05 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.052 }, + null, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.039 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { + "tactic": "split_vc", + "children": [ + null, { "prover": "alt-ergo@2.5.4", "time": 0.023 } + ] + } + ] + } + }, + "OPBatchNorm": { + "dbatchnorm": { "prover": "alt-ergo@2.5.4", "time": 0.008 }, + "opbatchnorm": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + null, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + { "prover": "alt-ergo@2.5.4", "time": 0.009 } + ] + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3session.xml.bak new file mode 100644 index 00000000..b2bf9af9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3session.xml.bak @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3shapes.gz.bak new file mode 100644 index 00000000..77ae16fe Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batch_normalization/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batchnorm_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batchnorm_driver.drv new file mode 100644 index 00000000..12c2b1f0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/batch_normalization/batchnorm_driver.drv @@ -0,0 +1,37 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module batch_normalization.CTensorBatchNorm + interface "#include " + interface "#include " + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + + syntax val csqrt "sqrt(%1)" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/Makefile b/safety-related-profile/sonnx/ops/spec/formal/clip/Makefile new file mode 100644 index 00000000..66bd2cb2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/clip/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'clip' +# Ce Makefile est exécuté depuis : ops/spec/formal/clip/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/clip/generated_doc +CODE_DIR = ../../../code/clip/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'clip' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'clip'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "clip operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_CLIP = -D c -D clip_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_CLIP) clip.TensorClip + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip.mlw b/safety-related-profile/sonnx/ops/spec/formal/clip/clip.mlw new file mode 100644 index 00000000..ad85e32e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/clip/clip.mlw @@ -0,0 +1,110 @@ +module OPClip + use tensor.Tensor + use int.Int + use list.List + use tensor.Range + use real.Real + use real.MinMax + + + predicate is_scalar_tensor (t : tensor real) = + t.dims = Nil /\ t.data = (fun _ -> t.background) + + predicate ordered_bounds (l m y: tensor real) = + forall ks:list int. valid ks y.dims -> + (l.background <= m.background -> + l.background <= y.data ks /\ y.data ks <= m.background) + + predicate inverted_bounds (l m y: tensor real) = + forall ks:list int. valid ks y.dims -> + (l.background > m.background -> + y.data ks = m.background) + + let ghost function dclip (x l m : tensor real): data real + (*proof*) + = fun ks -> + if valid ks x.dims then + min m.background (max (x.data ks) l.background) + else x.background + + let ghost function opclip (x l m : tensor real): tensor real + requires { is_scalar_tensor l } + requires { is_scalar_tensor m } + ensures { result ~= x } + ensures { ordered_bounds l m result } + ensures { inverted_bounds l m result } + (*proof*) + = { dims = x.dims; data = dclip x l m; background = x.background } + (*qed*) + + goal clip_correct: + forall x l m: tensor real. + is_scalar_tensor l /\ is_scalar_tensor m -> + let y = opclip x l m in + ordered_bounds l m y /\ inverted_bounds l m y + (* same as *) + (* + if l.background <= m.background then + ordered_bounds l m y + else + inverted_bounds l m y + *) + +end + +module ClipAux + use std.Cfloat + use real.MinMax + + val function (.>>) (a b : float) : float + ensures { Real.( result = max a b ) } + + val function (.<<) (a b : float) : float + ensures { Real.( result = min a b ) } +end + +module TensorClip + use std.Int + use std.List + use std.Clib + use std.Cfloat + use mach.int.Int32 + use tensor.Range + use tensor.Tensor + use tensor.OPWhere + use layout.CFlat + use libvector.CIndex + use libtensor.CTensor + use OPClip + use ClipAux + + let ctensor_clip (x l m r : ctensor) = + (*proof*) + requires { valid_tensor x } + requires { valid_tensor l } + requires { valid_tensor m } + requires { valid_tensor r } + requires { is_scalar_tensor (tensor l) } + requires { is_scalar_tensor (tensor m) } + requires { tensor x ~= tensor r } + (*qed*) + ensures { tensor r = opclip (tensor x) (tensor l) (tensor m) } + let n = cdim_size r.t_dims r.t_rank in + let l_backgroundl = value_at l.t_data 0 in + let m_backgroundl = value_at m.t_data 0 in + let l_background = l.t_data[0] in + let m_background = m.t_data[0] in + for i = 0 to n - 1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = (m_backgroundl .<< ( (value_at x.t_data k) .>> l_backgroundl)) + } + (*qed*) + r.t_data[i] <- m_background .<< ( (x.t_data[i]) .>> l_background) + done + (*proof*) + ; assert { tensor r == opclip (tensor x) (tensor l) (tensor m) } + (*qed*) + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml new file mode 100644 index 00000000..d38ddbce --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml.bak new file mode 100644 index 00000000..d38ddbce --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3session.xml.bak @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz new file mode 100644 index 00000000..218d72b4 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz.bak new file mode 100644 index 00000000..218d72b4 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/clip/clip/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/clip/clip_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/clip/clip_driver.drv new file mode 100644 index 00000000..f5358aa0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/clip/clip_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module clip.TensorClip + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/broadcast/broadcast.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/broadcast/broadcast.mlw new file mode 100644 index 00000000..0fe0cd3c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/broadcast/broadcast.mlw @@ -0,0 +1,11 @@ +module OPBroadcast + use tensor.tensor.Tensor + use tensor.Range + use list.List + use list.Length + + + let ghost function broadcast (lx : list tensor 'a) : list tensor 'a + + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/drivers/cdriver.drv b/safety-related-profile/sonnx/ops/spec/formal/common/drivers/cdriver.drv new file mode 100644 index 00000000..8532882a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/drivers/cdriver.drv @@ -0,0 +1,43 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module tensor.log.Log + syntax val log "/* %1 */" prec 0 +end + + +module tensor.libvector.CIndex + interface "#include " + interface "#include " +end + +module tensor.libtensor.CTensor + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + + +end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/drivers/libtensor.drv b/safety-related-profile/sonnx/ops/spec/formal/common/drivers/libtensor.drv new file mode 100644 index 00000000..ac9ddc12 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/drivers/libtensor.drv @@ -0,0 +1,3 @@ +module tensor.libtensor.CTensor + syntax type ctensor "struct ctensor" +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/README.md b/safety-related-profile/sonnx/ops/spec/formal/common/libs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/Makefile b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/Makefile new file mode 100644 index 00000000..35715567 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/Makefile @@ -0,0 +1,42 @@ +# Makefile for libvector + +.PHONY: all prove update doc lib cc clean +#Directories +DOC_DIR = ../../../../../code/common/libs/tensor/generated_doc +CODE_DIR = ../../../../../code/common/libs/tensor/generated_code/c_code +CDRIVER_DIR = ../../drivers + +all: prove doc lib + @echo "------------------------------------------" + +prove: + @echo "------------------------------------------" + @echo "--- Proofs" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +update: + why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Tensor Library" -o $(DOC_DIR) + +EXTRACT=-D c -D $(CDRIVER_DIR)/cdriver.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT) tensor.libvector.CIndex + @why3 extract $(EXTRACT) tensor.libtensor.CTensor + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +clean: + rm -fr $(CODE_DIR) + rm -f $(DOC_DIR) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/README.md b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/README.md new file mode 100644 index 00000000..cb07f3ad --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/README.md @@ -0,0 +1,93 @@ +# ONNX-C + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/topics/git/add_files/#add-files-to-a-git-repository) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://git.frama-c.com/lcorrenson/onnx-c.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://git.frama-c.com/lcorrenson/onnx-c/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Set auto-merge](https://docs.gitlab.com/user/project/merge_requests/auto_merge/) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README + +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout.mlw new file mode 100644 index 00000000..f09cf9be --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout.mlw @@ -0,0 +1,91 @@ +module CFlat + use tensor.std.Int + use tensor.std.List + use tensor.tensor.Range + + function offset (ks ds : list int) : int = + match ks , ds with + | Cons k ks , Cons _ ds -> k * size ds + offset ks ds + | _ -> 0 + end + + function index (p : int) (ds : list int) : list int = + match ds with + | Nil -> Nil + | Cons _ ds -> let n = size ds in Cons (div p n) (index (mod p n) ds) + end + + let rec lemma offset_size (ks ds : list int) + requires { valid ks ds } + ensures { 0 <= offset ks ds < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k krs , Cons d drs -> + offset_size krs drs ; + let p = offset ks ds in + let q = offset krs drs in + assert { p = size drs * k + q } ; + assert { + size drs * k + <= size drs * (d - 1) + = d * size drs - size drs + } ; + | _ -> () + end + (*qed*) + + let rec lemma index_range (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { valid (index p ds) ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> index_range (mod p (size rds)) rds + end + (*qed*) + + let rec lemma index_of_offset (ks ds : list int) + requires { valid ks ds } + ensures { index (offset ks ds) ds = ks } + (*proof*) + variant { ds } + = match ks , ds with + | Cons k rks , Cons _ rds -> + index_of_offset rks rds ; + let p = offset ks ds in + let n = size rds in + let q = offset rks rds in + euclide p k n q ; + | _ -> () + end + (*qed*) + + let rec lemma offset_of_index (p : int) (ds : list int) + requires { positive ds } + requires { 0 <= p < size ds } + ensures { offset (index p ds) ds = p } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons _ rds -> + let n = size rds in + offset_of_index (mod p n) rds + end + (*qed*) + + let rec lemma offset_push (ks ds : list int) (k d : int) + requires { valid ks ds } + ensures { offset (push ks k) (push ds d) = offset ks ds * d + k } + (*proof*) + variant { ks } + = match ks , ds with + | Cons _ krs , Cons _ drs -> offset_push krs drs k d + | _ -> () + end + (*qed*) + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/proof.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/proof.json new file mode 100644 index 00000000..523c7d86 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/proof.json @@ -0,0 +1,39 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CFlat": { + "index_of_offset": { "prover": "cvc5@1.2.1", "time": 0.072 }, + "index_range": { "prover": "alt-ergo@2.5.4", "time": 0.101 }, + "offset_of_index": { "prover": "alt-ergo@2.5.4", "time": 0.189 }, + "offset_push": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.056 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 1.4 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.043 } + ] + } + ] + }, + "offset_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "z3@4.13.4", "time": 0.013 }, + { "prover": "cvc5@1.2.1", "time": 0.102 } + ] + } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3session.xml.bak new file mode 100644 index 00000000..a20e714f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3session.xml.bak @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3shapes.gz.bak new file mode 100644 index 00000000..bffff867 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/layout/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor.mlw new file mode 100644 index 00000000..dd0490d4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor.mlw @@ -0,0 +1,116 @@ +module CTensor + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else 0.0 + + function tensor_value (t : ctensor) : list int -> real = + fun k -> tensor_value_at k t + + function tensor_valueb (t : ctensor) : list int -> bool = + fun k -> Real.(tensor_value_at k t > 0.0) + + let ghost function tensor (t : ctensor) : tensor real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = 0.0 ; + } + + let ghost function tensorb (t : ctensor) : tensor bool + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_valueb t } + ensures { result.background = false } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_valueb t } ; + background = false ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero 0.0 (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_real v) 0.0 (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_real v) 0.0 (tensor_dim r) } + (*qed*) + + + + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/proof.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/proof.json new file mode 100644 index 00000000..2aba3fe9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/proof.json @@ -0,0 +1,29 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.215 } + ], + "proofs": { + "CTensor": { + "ctensor_clear": { "prover": "alt-ergo@2.5.4", "time": 0.242 }, + "ctensor_create": { "prover": "alt-ergo@2.5.4", "time": 0.04 }, + "ctensor_reset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.046 }, + { "prover": "alt-ergo@2.5.4", "time": 0.075 }, + { "prover": "alt-ergo@2.5.4", "time": 0.027 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 } + ] + }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "tensorb": { "prover": "alt-ergo@2.5.4", "time": 0.033 } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3session.xml.bak new file mode 100644 index 00000000..afe6f419 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3session.xml.bak @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3shapes.gz.bak new file mode 100644 index 00000000..db5a5b3f Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libtensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector.mlw new file mode 100644 index 00000000..afdb2c0f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector.mlw @@ -0,0 +1,151 @@ +(** C-Library to compute Offsets & Dimensions *) + +module CIndex + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use mach.int.Int32 + use tensor.tensor.Range + use tensor.layout.CFlat + + type iarray = ptr int32 + + function ivector (u : iarray) (n : int) : list int + = map Int32.to_int (vector u n) + + function islice (u : iarray) (p q : int) : list int + = map Int32.to_int (slice u p q) + + (** ## Dimensions *) + + function vdim (u : iarray) (n : int) : int = size (ivector u n) + function sdim (u : iarray) (p q : int) : int = size (islice u p q) + + predicate pdim (u : iarray) (p q : int) = + forall k. p <= k < q -> 0 < value_at u k + + predicate dimension (u : iarray) (n : int) = + 0 <= n /\ valid_range u 0 n /\ pdim u 0 n /\ 0 < vdim u n <= max_int32 + + (** Equivalence betwen {pdim} and {tensor.Range.positive}. *) + let rec lemma positive_pdim (u : iarray) (p q : int) + (*proof*) + ensures { pdim u p q <-> positive (islice u p q) } + variant { q - p } + = if p < q then positive_pdim u (p+1) q + (*qed*) + + let ghost sdim_split (u : iarray) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { pdim u p q } + ensures { sdim u p k * value_at u k * sdim u (k+1) q = sdim u p q } + = assert { islice u p k ++ islice u k q = islice u p q } + (*qed*) + + (** Extracted Library *) + + let cdim_size (u : iarray) (n : int32): int32 = + ensures { result = vdim u n } + (*proof*) + requires { dimension u n } + (*qed*) + begin + let ref p = 1 in + for i = 0 to n - 1 do + (*proof*) + invariant { p = vdim u i } + ghost + begin + ensures { 1 <= p * u[i] <= max_int32 } + let ghost dl = pure { sdim u 0 i } in + let ghost di = Int32.to_int u[i] in + let ghost dr = pure { sdim u (i+1) n } in + let ghost dn = pure { sdim u 0 n } in + sdim_split u 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * u[i] ; + done ; p + end + + let cdim_create_1 (n : int32) : iarray = + requires { 0 < n } + ensures { is_not_null result -> vdim result 1 = n } + (*proof*) + ensures { is_not_null result -> dimension result 1 } + ensures { is_not_null result -> value_at result 0 = n } + (*qed*) + let cd = malloc 1 in + if is_not_null cd then cd[0] <- n ; cd + + let cdim_create_2 (p q : int32) : iarray = + requires { 0 < p /\ 0 < q /\ p * q <= max_int32 } + ensures { is_not_null result -> vdim result 2 = p * q } + (*proof*) + ensures { is_not_null result -> dimension result 2 } + ensures { is_not_null result -> value_at result 0 = p } + ensures { is_not_null result -> value_at result 1 = q } + (*qed*) + let cd = malloc 2 in + if is_not_null cd then (cd[0] <- p ; cd[1] <- q) ; cd + + (** ## Coordinates *) + + let rec lemma valid_bounds (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { valid (islice ks p q) (islice ds p q) } + ensures { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + assert { 0 <= value_at ks p < value_at ds p }; + valid_bounds ks ds (p+1) q + end + + let coffset (ks : iarray) (ds : iarray) (n : int32) : int32 = + (*proof*) + requires { 0 <= n } + requires { valid_range ks 0 n } + requires { dimension ds n } + ensures { 0 <= result <-> valid (ivector ks n) (ivector ds n) } + ensures { 0 <= result -> result = offset (ivector ks n) (ivector ds n) } + (*qed*) + begin + let ref p = 0 in + for i = 0 to n - 1 do + (*proof*) + invariant { valid (ivector ks i) (ivector ds i) } + invariant { p = offset (ivector ks i) (ivector ds i) } + (*qed*) + let d = ds[i] in + let k = ks[i] in + if 0 <= k && k < d then + begin + (*proof*) + assert { p * d + k = offset (ivector ks (i+1)) (ivector ds (i+1)) } ; + ghost + begin + ensures { Int32.in_bounds (p * d) } + ensures { Int32.in_bounds (p * d + k) } + let ghost dl = pure { sdim ds 0 i } in + let ghost di = Int32.to_int ds[i] in + let ghost dr = pure { sdim ds (i+1) n } in + let ghost dn = pure { sdim ds 0 n } in + sdim_split ds 0 (Int32.to_int i) (Int32.to_int n) ; + mult_bound (dl * di) dr dn ; + end ; + (*qed*) + p <- p * d + k ; + end + else return (-1) + done ; p + end + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/proof.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/proof.json new file mode 100644 index 00000000..1d48b8b6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/proof.json @@ -0,0 +1,91 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "CIndex": { + "cdim_create_1": { "prover": "alt-ergo@2.5.4", "time": 0.158 }, + "cdim_create_2": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.262 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.023 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 } + ] + }, + "cdim_size": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.035 }, + { "prover": "alt-ergo@2.5.4", "time": 0.626 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.126 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 } + ] + }, + "coffset": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.025 }, + { "prover": "alt-ergo@2.5.4", "time": 0.024 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 }, + { "prover": "alt-ergo@2.5.4", "time": 0.835 }, + { "prover": "alt-ergo@2.5.4", "time": 0.034 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.065 }, + { "prover": "alt-ergo@2.5.4", "time": 0.152 }, + { + "tactic": "split_vc", + "children": [ + { + "tactic": "inline_goal", + "children": [ + { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, null + ] + } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 } + ] + }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.902 }, + { "prover": "alt-ergo@2.5.4", "time": 0.02 }, + { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.016 } + ] + }, + "positive_pdim": { "prover": "alt-ergo@2.5.4", "time": 0.069 }, + "sdim_split": { "prover": "alt-ergo@2.5.4", "time": 0.17 } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3session.xml.bak new file mode 100644 index 00000000..5989930f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3session.xml.bak @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3shapes.gz.bak new file mode 100644 index 00000000..a3f8a366 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/libvector/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/log.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/log.mlw new file mode 100644 index 00000000..17ff0b2d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/log.mlw @@ -0,0 +1,6 @@ +module Log + use string.String + type world = abstract { mutable universe : int } + val ghost world : world + val log string : unit writes { world } +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std.mlw new file mode 100644 index 00000000..42ffdf98 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std.mlw @@ -0,0 +1,232 @@ +(* Extensions to Standard Library *) + +module Int + use export int.Int + use export int.Abs + use export int.ComputerDivision + use list.List + use list.Length + + (** Unicity of euclidian division decomposition *) + let ghost euclide (a b q r : int) + requires { 0 <= a /\ 0 <= r < q } + requires { a = b * q + r } + ensures { b = div a q } + ensures { r = mod a q } + (*proof*) + = let rec kernel (b r : int) + requires { b * q + r = 0 } + requires { abs r < q } + ensures { b = r = 0 } + variant { abs b } + = if b < 0 then kernel (b + 1) (r - q) else + if b > 0 then kernel (b - 1) (r + q) else () + in kernel (b - div a q) (r - mod a q) + (*qed*) + + (** Multiplication upper-bound *) + let ghost mult_bound (a b c : int) + requires { 0 < a * b <= c } + ensures { abs a <= c } + ensures { abs b <= c } + = () + + (** Helper function to extract element from list at given position *) + let rec function get_dim (dims : list int) (idx : int) : int + requires { 0 <= idx < length dims } + variant { dims } + = match dims with + | Nil -> 0 (* should not happen due to precondition *) + | Cons h t -> if idx = 0 then h else get_dim t (idx - 1) + end + +end + +module List + use export list.List + use export list.Map + use export list.Append + + (** Appends an element to the end of the list *) + function push (xs : list 'a) (x : 'a) : list 'a = + xs ++ Cons x Nil + + let rec lemma map_concat (f : 'a -> 'b) (xs ys : list 'a) + ensures { map f (xs ++ ys) = map f xs ++ map f ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> map_concat f rxs ys | Nil -> () end + (*qed*) + +end + +module Clib + use map.Map + use int.Int + use mach.int.Int32 + use export mach.c.C + + type int32 = Int32.int32 + type uint32 = UInt32.uint32 + + val function to_uint32 (v : int32) : uint32 + requires { 0 <= v } + ensures { Int32.to_int v = UInt32.to_int result } + + (** Null-Pointer *) + val predicate is_null (vp : ptr 'a) : bool + ensures { result <-> vp.zone = null_zone } + + (** Array-range validity *) + predicate valid_range (vp : ptr 'a) (p q : int) = + q <= p \/ + ( 0 <= vp.min <= vp.offset /\ + 0 <= p <= q <= max_int32 /\ + 0 <= vp.min <= vp.offset + p /\ + vp.offset + q <= vp.max <= vp.plength ) + + let lemma valid_in_range (vp : ptr 'a) (p k q : int) + (*proof*) + requires { p <= k < q } + requires { valid_range vp p q } + ensures { valid_ptr_shift vp k } + = () + (*qed*) + + (** Array as map *) + let ghost function value_at (vp : ptr 'a) : map int 'a = + pure { fun k -> vp.data.Array.elts (vp.offset + k) } + + (** Array access (ghost) *) + function ([]) (vp : ptr 'a) (k : int) : 'a = value_at vp k + + (** Array access (C, inlined) *) + let ([]) (vp : ptr 'a) (k : int32) : 'a + (*proof*) + requires { vp.min <= vp.offset + k < vp.max } + ensures { result = vp[k] } + = get_ofs vp k + (*qed*) + + (** Array update (C, inlined) *) + let ([]<-) (vp : ptr 'a) (k : int32) (v : 'a) : unit + requires { writable vp } + requires { vp.min <= vp.offset + k < vp.max } + ensures { value_at vp = Map.([<-]) (old (value_at vp)) k v } + = set_ofs vp k v + + (** Array as list *) + use List + + let rec ghost function slice (u : ptr 'a) (p q : int) : list 'a = + (*proof*) + variant { q - p } + (*qed*) + if q <= p then Nil else Cons (value_at u p) (slice u (p+1) q) + + let rec lemma slice_append (u : ptr 'a) (p q r : int) + requires { p <= q <= r } + ensures { slice u p r = slice u p q ++ slice u q r } + (*proof*) + variant { q - p } + = if p < q then slice_append u (p+1) q r + (*qed*) + + let ghost function vector (u : ptr 'a) (n : int) = slice u 0 n + + let lemma vector_push (u : ptr 'a) (n : int) + requires { 0 <= n } + ensures { vector u (n+1) = push (vector u n) (value_at u n) } + (*proof*) + = slice_append u 0 n (n+1) + (*qed*) + +val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + +end + + +module Cint + use int.Int + use mach.int.Int32 + use mach.int.Int32 as Int32 + use mach.int.UInt32 + use mach.int.UInt32 as UInt32 + use int.ComputerDivision + use Clib + + val function ( .+ ) (a b : int) : int + ensures { result = a + b } + + val function ( .- ) (a b : int) : int + ensures { result = a - b } + + val function ( .* ) (a b : int) : int + ensures { result = a * b } + + val function ( ./ ) (a b : int) : int + requires { b <> 0 } + ensures { result = ComputerDivision.div (a) ( b) } + + val function of_int (v : int) : Int32.int32 + ensures { Int32.to_int result = v } + + val function malloc_int (s: uint32) : ptr int + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + val function get (p: ptr int) (k: Int32.int32) : int + ensures { result = p[k] } + + val function malloc_int32 (s: uint32) : ptr int32 + ensures { is_null result || valid_range result 0 (UInt32.to_int s) } + + let ([]) (p : ptr int) (k :Int32.int32) : int = get p k + +end + +module Cfloat + use int.Int + use real.Real + + type float = abstract { to_real : real } + meta coercion function to_real + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + val function f32 (k : f32) : float + ensures { result = f32'real k } + + val function f64 (k : f64) : float + ensures { result = f64'real k } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { Real.( result = a + b ) } + + val function (.-) (a b : float) : float + ensures { Real.( result = a - b ) } + + val function (.-_) (a : float) : float + ensures { Real.( result = (- a) ) } + + val function ( .* ) (a b : float) : float + ensures { Real.( result = a * b ) } + + val function ( ./ ) (a b : float) : float + requires { b <> 0.0 } + ensures { Real.( result = a / b ) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> Real.( a = b ) } + + val predicate ( .< ) (a b : float) : bool + ensures { result <-> Real.( a < b ) } + + val predicate ( .<= ) (a b : float) : bool + ensures { result <-> Real.( a <= b ) } + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/proof.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/proof.json new file mode 100644 index 00000000..25e5ff60 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/proof.json @@ -0,0 +1,26 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 33, "time": 0.865 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.492 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "Cfloat": { + "one": { "tactic": "split_vc", "children": [] }, + "zero": { "tactic": "split_vc", "children": [] } + }, + "Clib": { + "mixfix []": { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + "mixfix []<-": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "slice": { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + "slice_append": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "valid_in_range": { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + "vector_push": { "prover": "z3@4.13.4", "time": 0.016 } + }, + "Int": { + "euclide": { "prover": "alt-ergo@2.5.4", "time": 0.047 }, + "mult_bound": { "prover": "z3@4.13.4", "time": 0.006 } + }, + "List": { "map_concat": { "prover": "alt-ergo@2.5.4", "time": 0.018 } } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3session.xml.bak new file mode 100644 index 00000000..0f21f2d9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3session.xml.bak @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3shapes.gz.bak new file mode 100644 index 00000000..1290ad2f Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/std/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor.mlw b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor.mlw new file mode 100644 index 00000000..60536b86 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor.mlw @@ -0,0 +1,141 @@ +(** Formalization of coordinates and dimensions *) + +module Range + use int.Int + use tensor.std.List + + function size (ds : list int) : int = + match ds with + | Nil -> 1 + | Cons d ds -> d * size ds + end + + predicate positive (ds : list int) = + match ds with + | Nil -> true + | Cons d ds -> 0 < d /\ positive ds + end + + predicate valid (ks ds : list int) = + match ks , ds with + | Nil , Nil -> true + | Cons k ks , Cons d ds -> 0 <= k < d /\ valid ks ds + | _ -> false + end + + let rec lemma valid_push (ks ds : list int) (k d : int) + requires { 0 <= k < d } + requires { valid ks ds } + ensures { valid (push ks k) (push ds d) } + (*proof*) + variant { ks } + = match ks , ds with + | Nil , Nil -> () + | Cons _ ks , Cons _ ds -> valid_push ks ds k d + | _ -> absurd + end + (*qed*) + + let rec lemma size_append (xs ys : list int) + ensures { size (xs ++ ys) = size xs * size ys } + (*proof*) + variant { xs } + = match xs with Cons _ rxs -> size_append rxs ys | Nil -> () end + (*qed*) + + lemma size_push: forall xs x. size (push xs x) = size xs * x + + let rec lemma positive_size (ds : list int) + requires { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ds with Nil -> () | Cons _ ds -> positive_size ds end + (*qed*) + + let rec lemma positive_valid (ks ds : list int) + requires { valid ks ds } + ensures { positive ds } + ensures { 0 < size ds } + (*proof*) + variant { ds } + = match ks , ds with + | Cons _ ks , Cons _ ds -> positive_valid ks ds + | _ -> () + end + (*qed*) + +end + +(** Formalization of Tensor *) +module Tensor + use int.Int + use map.Map + use list.List + use Range + + type data 'a = map (list int) 'a + + type tensor 'a = { + dims : list int ; + data : data 'a ; + background : 'a ; (* default value, or value for 0-dimensions tensor *) + } + invariant { positive dims } + invariant { forall k. valid k dims \/ data k = background } + (*proof*) + by let d = any 'a in { dims = Nil ; data = (fun _ -> d) ; background = d } + (*qed*) + + meta coercion function dims + meta coercion function data + + (** Tensor with the same dimensions *) + predicate (~) (a : tensor 'a) (b : tensor 'b) = a.dims = b.dims + + (** Tensor with the same dimensions and background value *) + predicate (~=) (a : tensor 'a) (b : tensor 'a) = + a ~ b /\ a.background = b.background + + (** Equal tensors *) + predicate (==) (a b : tensor 'a) = + a ~= b /\ forall k. valid k a.dims -> a.data k = b.data k + + (** Extensionality *) + lemma exteq: forall a b : tensor 'a. a == b <-> a = b + (*proof*) by tensor'eq a b (*qed*) + + (** Scalar Tensor *) + let ghost function scalar (v : 'a) : tensor 'a + ensures { result.dims = Nil } + ensures { result.background = v } + ensures { forall k. result k = v } + (*proof*) + = { dims = Nil ; data = (fun _ -> v) ; background = v } + (*qed*) + + (** Null Tensor *) + let ghost function zero (e : 'a) (ds : list int) : tensor 'a + requires { positive ds } + ensures { result.dims = ds } + ensures { result.background = e } + ensures { forall k. result k = e } + (*proof*) + = { dims = ds ; data = (fun _ -> e) ; background = e } + (*qed*) + + + (** Constant Tensor *) + let ghost function const (v : 'a) (bg : 'a) (ds : list int): tensor 'a + ensures { result.dims = ds } + requires { positive ds } + ensures { result.background = bg } + ensures { forall k. valid k ds -> result k = v } + (*proof*) + = { dims = ds ; data = pure { fun k -> if valid k ds then v else bg } ; background = bg } + (*qed*) + + (** Constant & Null *) + goal zero_is_const: forall e : 'a, ds. positive ds -> zero e ds == const e e ds + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/proof.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/proof.json new file mode 100644 index 00000000..2e8fec4a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/proof.json @@ -0,0 +1,35 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 30, "time": 0.14 }, + { "prover": "cvc5@1.2.1", "size": 42, "time": 0.489 }, + { "prover": "alt-ergo@2.5.4", "size": 16, "time": 0.171 } + ], + "proofs": { + "OPWhere": { + "dwhere": { "tactic": "split_vc", "children": [] }, + "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.015 } + }, + "Range": { + "positive_size": { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + "positive_valid": { "prover": "alt-ergo@2.5.4", "time": 0.029 }, + "size_append": { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + "size_push": { "prover": "alt-ergo@2.5.4", "time": 0.011 }, + "valid_push": { "prover": "alt-ergo@2.5.4", "time": 0.032 } + }, + "Tensor": { + "const": { "prover": "alt-ergo@2.5.4", "time": 0.017 }, + "exteq": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 } + ] + }, + "scalar": { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + "tensor": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "zero": { "prover": "alt-ergo@2.5.4", "time": 0.012 }, + "zero_is_const": { "prover": "alt-ergo@2.5.4", "time": 0.012 } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3session.xml.bak new file mode 100644 index 00000000..7b8a58b7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3session.xml.bak @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3shapes.gz.bak new file mode 100644 index 00000000..cc100ad1 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/tensor/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3.conf b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3.conf new file mode 100644 index 00000000..d8bb255d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3.conf @@ -0,0 +1,5 @@ +# ========================================================== +# Configuration du package Why3 : tensor +# ========================================================== +[package tensor] +path = . diff --git a/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3find.json b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/common/libs/tensor/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/concat/concat.mlw b/safety-related-profile/sonnx/ops/spec/formal/concat/concat.mlw new file mode 100644 index 00000000..ad4d7b5a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/concat/concat.mlw @@ -0,0 +1,149 @@ +module Concat + + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + use sequence.Seq + use list.ListRich as L + + + (* =================== Concat shape ================================*) + (** T6: defining s_k which is the sum of the different tensors dimension along the axis considered **) + (* + x_k: list (sequence) of input tensors + axis: axis along which the concatenation is performed + *) + let rec function compute_s_k_at_axis (x_k: seq (tensor 'a)) (axis: int) : int = + variant{x_k} (* a_k has to decrease size *) + match x_k with + | L.Nil -> 0 + | L.Cons hd tl -> (hd.shape.dims)[axis] + compute_s_k_at_axis tl axis + end + + (** T9: computing the shape (dimensions) of the output concatenated tensor **) + (* + x_k: list (sequence) of input tensors + axis: axis along which the concatenation is performed + idx: The current index in an input tensor shape.dims sequence; idx must be 0 (zero) at the first call of output_dims; + it is incremented at each recursive call of output_dims + nb_dims: number of dimensions of an input tensor + *) + let rec function output_dims (x_k: seq (tensor 'a)) (axis: int) (idx: int) (nb_dims: int) : seq int = + (* Stop case: Finished building *) + variant { nb_dims - idx } + if idx >= nb_dims then + Seq.empty + else + let value_for_idx = + if idx = axis then + compute_s_k_at_axis x_k axis (* Computes the s_k (sum of dimensions along the axis) for all k tensors*) + else + (x_k[0]).shape.dims[idx] (* Copy of the value from x_k[0].shape.dims[i], without paying attention to the i' considered because according to (T8) all tensors have the same shape *) + in + (* Combine: output_dims[current_idx] followed by the rest*) + (L.Cons value_for_idx (output_dims x_k axis (idx + 1) nb_dims) : seq int) + + + (** T9: computing the shape (dimensions) of the output concatenated tensor **) + (* + x_k: list (sequence) of input tensors + axis: axis along which the concatenation is performed + *) + let function output_shape (x_k: seq (tensor 'a)) (axis : int): shape = + (* The dimension of a_k[0] was selected because according to (T7) the input tensors have the same shape => same length *) + ensures {length result.dims = Tensor.dim x_k[0]} + { dims = (output_dims x_k axis 0 (Tensor.dim x_k[0]))} + + + (*======================= Concat value ================================*) + + (** T3: finding the right k for a local tensor among input tensors x_k **) (* TODO: Spliting find k and i prime in two separate functions *) + (** T5: defining the local index i' for a i (global index) given **) + (* + seq_D_axis: seq_D_axis is the sequence of all the d_k,axis for each k tensor + i_axis: value on the axis along which the concatenation is performed + k: Current index in sequence seq_D_axis. Must be 0 at the first call of the function + s_k: Current sum of the previous dimensions in seq_D_axis. Must be 0 at the first call of the function + *) + let rec rec_find_k_and_i_prime (seq_D_axis: seq int) (i_axis: int) (k: int) (s_k: int) : (int, int) + variant { length seq_D_axis - k } (* Termination measure *) + = + if i_axis < s_k + seq_D_axis[k] then (* Inequality (T4) to define to in which k tensor the local index i' is defined *) + (k, (i_axis - s_k)) (* Definition (T4) by keeping the upper part of the inequality: i' = i - s_k *) + else + (* The global index i is superior to s_k + seq_D_axis[k] so the k tensor to define the local index i' is not the current one but next one k+1. Update the offset (s_k) by adding the length of the tensor we just checked (current tensor). *) + rec_find_k_and_i_prime seq_D_axis i_axis (k + 1) (s_k + seq_D_axis[k]) + + (** T2, T3 and T5: defining the local index i' for a i (global index) given and computing the k associated to i'**) + (* + seq_D_axis: seq_D_axis is the sequence of all the D_k^axis for each k tensor + i_axis: value on the axis along which the concatenation is performed + *) + let find_k_and_i_prime (seq_D_axis: seq int) (i_axis: int) : (int, int) = rec_find_k_and_i_prime seq_D_axis i_axis 0 0 + + (** Utils function to copy the actual index i and changing the value for the axis by i'*) + (* + s: seq `a is the sequence to copy + idx: value of the index equals to the axis + value: value at replace at the idx position + *) + let rec list_update (s: seq 'a) (idx: int) (value: 'a) : seq 'a (** return my local index i' with the value along the axis changed**) + requires { 0 <= idx < length s } + ensures { length result = length s } + variant { idx } + = match s with + | L.Nil -> L.Nil + | L.Cons x xs -> if idx = 0 then + L.Cons value xs : seq 'a + else + L.Cons x (list_update xs (idx - 1) value) + end + + (** T2: defining the mathematical formula that defines the concat operator**) + (* + x_k: list (sequence) of input tensors + axis: axis along which the concatenation is performed + *) + let function output_value (x_k: seq (tensor 'a))(axis: int): (index -> 'a) + (* ensures {pred_value_spec_concat x_k axis result} *) + + = + let seq_D_axis = map (fun t -> t.shape.dims[axis]) x_k in + + (* global indices i for my return value function *) + fun (global_index: index) -> + + let i_axis = global_index[axis] in + let (k, i_prime) = find_k_and_i_prime seq_D_axis i_axis in + let local_index_k = list_update global_index axis i_prime in + let source_tensor_k = x_k[k] in + let result_value = source_tensor_k.value local_index_k in + + (*Return the computed value *) + result_value + + + + (** (T1...T9): function that defines the formal definition of concat in informal specification **) + (* + x_k: list (sequence) of input tensors + axis: axis along which the concatenation is performed + *) + let function concat (x_k: seq (tensor 'a)) (axis: int) : tensor 'a + requires{1 <= length x_k <= 2147483647} (** T1: bounded number of inputs **) + requires { forall k: int. (0<= k < length x_k) -> 0 <= axis < Tensor.dim x_k[k] } (** C1: bounded axis value **) + requires { forall k: int. (0<= k < (Seq.length x_k)- 1) -> Tensor.dim x_k[k] = Tensor.dim x_k[k+1] } (** T7: inputs having the same shape => same length **) + requires { forall k i: int. + (0 <= k /\ k < (Seq.length x_k)- 1 )/\((0<= i < Tensor.dim x_k[k]) /\ (i <> axis)) -> x_k[k].shape.dims[i]=x_k[k+1].shape.dims[i] } (** T8: inputs dimensions values match except along the axis **) + + = + { + shape = (output_shape x_k axis); (*T9: definition of the output shape for the concatenated tensor *) + value = (output_value x_k axis); (*T2: mathematical defintion of the operator concat*) + } + + +end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/concat/sequence.mlw b/safety-related-profile/sonnx/ops/spec/formal/concat/sequence.mlw new file mode 100644 index 00000000..5cfac493 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/concat/sequence.mlw @@ -0,0 +1,274 @@ +(*@ # Finite Sequences + + This modules provides a persistent array interface to lists. + It is typically used to model byte arrays of COSMO implementation. +*) + +module Seq + + use int.Int + use int.MinMax + use option.Option + use list.ListRich as L + + (*@ ## Definition + + A sequence `s` is defined by its size `n = length s` and its elements + `s[i]` with `0 <= i < n`. + + All other operations on sequences are defined + in terms of the length and elements of the returned sequences. + *) + + type seq 'a = L.list 'a + + (*@ Length of a sequence. *) + let function length (u : seq 'a) = L.length u + meta coercion function length + + (*@ Elements of a sequence. *) + let rec function ([]) (u : seq 'a) (k : int) : 'a + requires { 0 <= k < length u } + ensures { L.nth k u = Some result } + (*proof*) + variant { u } + = match u with L.Cons x w -> if k = 0 then x else w[k-1] end + (*qed*) + + lemma head: forall x:'a, u. (L.Cons x u)[0] = x + lemma tail: forall x:'a, u k. 0 < k <= length u -> (L.Cons x u)[k] = u[k-1] + + (*@ Definitionnal equality: `u == v` states that sequences `u` and `v` have + the same length and same elements. *) + predicate (==) (u v : seq 'a) = + length u = length v /\ forall k. 0 <= k < length u -> u[k] = v[k] + + (*@ Equal lists are definitionnaly equals. *) + lemma reflexivity : forall u : seq 'a. u == u + + (*@ Definitionnaly equal lists _are_ equals. *) + let rec lemma extensivity (a b : seq 'a) + requires { a == b } + ensures { a = b } + (*proof*) + variant { a, b } + = match a, b with + | L.Cons x a' , L.Cons y b' -> + assert { x = y } ; + let lemma shift (k: int) + requires { 0 <= k < length a' } + ensures { a'[k] = b'[k] } + = assert { a'[k] = a[k+1] } ; + assert { b'[k] = b[k+1] } + in extensivity a' b' + | _ -> () + end + (*qed*) + + (*@ Functional equality. *) + let rec equal (eq : 'a -> 'a -> bool) (u v : seq 'a) : bool + requires { forall x y. L.mem x u -> L.mem y v -> (eq x y <-> x = y) } + ensures { result <-> u = v } + (*proof*) + variant { u, v } + = match u, v with + | L.Cons x u', L.Cons y v' -> eq x y && equal eq u' v' + | L.Nil, L.Nil -> true + | _ -> false + end + (*qed*) + + (*@ ## Constructors + + Helper functions for constructing sequences. + *) + + (*@ Empty sequence of length `0`. *) + let constant empty : seq 'a = L.Nil + + (*@ Singleton sequence with a unique element. *) + let function elt (e : 'a) : seq 'a = L.Cons e L.Nil + + (*@ Head element *) + let function hd (s : seq 'a) : 'a + requires { 0 < s.length } + ensures { result = s[0] } + (*proof*) = match s with L.Cons x _ -> x end (*qed*) + + (*@ Tail element *) + let function tl (s : seq 'a) : 'a + requires { 0 < s.length } + ensures { result = s[ s.length - 1 ] } + (*proof*) = s[ s.length - 1 ] (*qed*) + + (*@ `create e n` returns a sequence of length `n` filled with `e` elements. *) + let rec function create (e : 'a) (n : int) : seq 'a + requires { 0 <= n } + ensures { length result = n } + ensures { forall k. 0 <= k < result -> result[k] = e } + (*proof*) + variant { n } + = if n > 0 then L.Cons e (create e (n-1)) else L.Nil + (*qed*) + + (*@ `reset e s` returns a sequence of same length `s` filled with `e` elements. *) + let function reset (e : 'a) (s : seq 'b) : seq 'a = create e s.length + + (*@ `init f a b` returns the sequence of elements `f i` for `a <= i < b`. *) + let rec function init (f : int -> 'a) (a b : int) : seq 'a + requires { a <= b } + ensures { length result = b-a } + ensures { forall k. 0 <= k < result -> result[k] = f (a+k) } + (*proof*) + variant { b-a } + = if a < b then L.Cons (f a) (init f (a+1) b) else L.Nil + (*qed*) + + (*@ `map f s` applies `f` to all elements f `s` *) + let rec function map (f : 'a -> 'b) (s : seq 'a) : seq 'b + ensures { length result = length s } + ensures { forall k. 0 <= k < result -> result[k] = f s[k] } + (*proof*) + variant { s } + = match s with L.Nil -> L.Nil | L.Cons x xs -> L.Cons (f x) (map f xs) end + (*qed*) + + (*@ ## Slices + + Sequences are commonly operated by a slice of their elements. + By convention, a slice `i..j` of elements denotes the range of elements + from `i` (_included_) to `j` (_excluded_), hence the range of indices `k` + such that `i <= k < j`. + + The slice of all elements of a sequence of size `n` is then `0..n`, and + we also denote slice `0..i` by `..i` and slice `i..n` by `i..`. + Slice operations are properly defined only for a valid range of indices. + + The dual operation of slice is the concatenation, here denoted by `(++)`. + *) + + (*@ `u ++ v` is the concatenation of `u` and `v`, + where `u` elements comes first, and `v` elements follows. *) + let rec function (++) (u v : seq 'a) : seq 'a + ensures { length result = length u + length v } + ensures { forall k. 0 <= k < u -> result[k] = u[k] } + ensures { forall k. u <= k < result -> result[k] = v[k - u] } + (*proof*) + variant { u } + = match u with L.Nil -> v | L.Cons x w -> L.Cons x (w ++ v) end + (*qed*) + + (*@ `u[i..j]` is the slice of `u` elements + from index `i` (_included_) to index `j` (_excluded_). *) + let rec function ([..]) (u : seq 'a) (i j : int) : seq 'a + requires { 0 <= i <= j <= u } + ensures { length result = j-i } + ensures { forall k. 0 <= k < result -> result[k] = u[i+k] } + (*proof*) + variant { u } + = match u with + | L.Nil -> L.Nil + | L.Cons x w -> + if 0 < i then w[i-1..j-1] else + if 0 < j then L.Cons x w[0..j-1] + else L.Nil + end + (*qed*) + + (*@ `u[..i]` is the slice of `u` elements up to index `i` (_excluded_). *) + let function ([.._]) (u : seq 'a) (i : int) : seq 'a + requires { 0 <= i <= u } + ensures { length result = i } + ensures { forall k. 0 <= k < result -> result[k] = u[k] } + = u[0..i] + + (*@ `u[i..]` is the slice of `u` elements from index `i` (_included_). *) + let function ([_..]) (u : seq 'a) (i : int) : seq 'a + requires { 0 <= i <= u } + ensures { result = u - i } + ensures { forall k. 0 <= k < result -> result[k] = u[i+k] } + = u[i..length u] + + (*@ This property illustrates the duality of slice & concat. *) + goal split: forall u : seq 'a, i. + 0 <= i <= u -> + u[..i] ++ u[i..] = u + (*proof*) + by u[..i] ++ u[i..] == u + (*qed*) + + (*@ This property illustrates the duality of slice & concat. *) + goal fullsplit: forall u : seq 'a, i j. + 0 <= i <= j <= u -> + u[..i] ++ u[i..j] ++ u[j..] = u + (*proof*) + by u[..i] ++ u[i..j] ++ u[j..] == u + (*qed*) + + (*@ Tail insertion *) + let function (+.) (s : seq 'a) (e : 'a) : seq 'a + ensures { length result = length s + 1 } + ensures { forall k. 0 <= k < s -> result[k] = s[k] } + ensures { result[ s ] = e } + (*proof*) = s ++ elt e (*qed*) + + (*@ Head insertion *) + let function (.+) (e : 'a) (s : seq 'a) : seq 'a + ensures { length result = length s + 1 } + ensures { forall k. 0 <= k < s -> result[1+k] = s[k] } + ensures { result[ 0 ] = e } + (*proof*) = elt e ++ s (*qed*) + + (*@ ## Updates & Copies *) + + (*@ `u[i<-x]` returns a copy of the sequence `u` where the element `u[i]` + has been replaced by `x`. *) + let rec function ([<-]) (u : seq 'a) (i : int) (x : 'a) : seq 'a + requires { 0 <= i < u } + ensures { length result = length u } + ensures { forall k. 0 <= k < result -> result[k] = if k=i then x else u[k] } + (*proof*) + variant { u } + = match u with L.Cons x0 w -> + if i = 0 then L.Cons x w else L.Cons x0 w[ i-1 <- x ] + end + (*qed*) + + (*@ `memcpy u i v j n` returns a copy of `u` where the slice `u[i..i+n]` has been + replaced with the slice `v[j..j+n]` (last excluded). *) + let function memcpy (u : seq 'a) (i : int) (v : seq 'a) (j : int) (n : int) + requires { 0 <= i <= i+n <= u } + requires { 0 <= j <= j+n <= v } + ensures { length result = length u } + ensures { forall k. 0 <= k < i -> result[k] = u[k] } + ensures { forall k. i <= k < i+n -> result[k] = v[j+k-i] } + ensures { forall k. i+n <= k < u -> result[k] = u[k] } + = u[..i] ++ v[j..j+n] ++ u[i+n..] + + (*@ `havoc u v p n` asserts that sequences `u` and `v` have identical elements + but elements in range `p..p+n` (last excluded). *) + predicate havoc (u v: seq 'a) (p n : int) = + u.length = v.length /\ + (forall k. 0 <= k < p -> u[k] = v[k]) /\ + (forall k. p+n <= k < u -> u[k] = v[k]) + +end + +(*@ ## Sequence Codomain *) + +module Codomain + + use int.Int + use set.Set + use Seq + + let rec ghost function codomain (s : seq 'a) : set 'a + ensures { forall v. Set.mem v result <-> exists i. 0 <= i < s /\ s[i] = v } + (*proof*) + variant { s.length } + = if s.length = 0 then Set.empty else + let k = s.length-1 in + Set.add s[k] (codomain s[0..k]) + (*qed*) + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/concat/tensor.mlw b/safety-related-profile/sonnx/ops/spec/formal/concat/tensor.mlw new file mode 100644 index 00000000..42c64d54 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/concat/tensor.mlw @@ -0,0 +1,132 @@ +(** + ONNX Tensor Definition. + *) + +module Shape + use int.Int + use list.Mem + use list.List + use sequence.Seq + + predicate positive (ds : seq int) = + forall i. 0 <= i < length ds -> 0 < ds[i] + + type shape = { dims : seq int } + (**invariant { positive dims }**) + meta coercion function dims + + (* Prédicat vérifiant si deux formes sont compatibles pour une concaténation + le long de 'axis'. *) + + (*proof*) + (* Helper function for defining product *) + let rec function product (ds : list int) : int = + variant { ds } + match ds with + | Nil -> 1 + | Cons d ds -> d * product ds + end + + (* Lemma: shifted sequence *) + let ghost shift (xs : list 'a) (x : 'a) (rxs : list 'a) + requires { xs = Cons x rxs } + ensures { x = xs[0] } + ensures { forall i. 0 <= i < length rxs -> rxs[i] = xs[i+1] } + = () + + (* Lemma: forall s, 1 <= product s *) + let rec lemma product1 (ds : list int) + requires { positive ds } + ensures { 1 <= product ds } + (*proof*) + variant { ds } + = match ds with + | Nil -> () + | Cons d rds -> + ghost shift ds d rds ; + product1 rds + end + (*qed*) + (*qed*) + + let sizeof (s : shape) : int = product s.dims + +end + +module Index + use int.Int + use bool.Bool + use list.Mem + use list.List + use sequence.Seq + use Shape + + type index = seq int + + predicate valid (idx : index) (s : shape) = + length idx = length s /\ + forall i. 0 <= i < length s -> 0 <= idx[i] < s[i] + + let valid (idx : index) (s : shape) : bool + ensures { result <-> valid idx s } + (*proof*) + (* Efficient Implementation *) + = let rec inrange (ks ds : list int) : bool + requires { positive ds } + ensures { result <-> + ( length ks = length ds /\ + forall i. 0 <= i < length ks -> 0 <= ks[i] < ds[i] ) + } + variant { ds } + = match ks, ds with + | Nil, Nil -> true + | Cons k ksr, Cons d dsr -> + ghost shift ks k ksr ; + ghost shift ds d dsr ; + 0 <= k < d && inrange ksr dsr + | _ -> false + end + in inrange idx s.dims + (*qed*) + +end + +module Tensor + use int.Int + use sequence.Seq + use export Shape + use export Index + + type value 'a = index -> 'a + + type tensor 'a = { + shape : shape ; + value : value 'a; + } + + let function dim (t : tensor 'a) : int = length t.shape.dims + + let function mk_shape (d: seq int) : shape = + { dims = d } + + + predicate (<<) (idx : index) (t : tensor 'a) = valid idx t.shape + function ([]) (t : tensor 'a) (idx : index) : 'a = t.value idx + + exception Invalid_index + + let mem (idx : index) (t : tensor 'a) : bool + ensures { result <-> idx << t } + (*proof*) + = valid idx t.shape + (*qed*) + + let get (idx : index) (t : tensor 'a) : 'a + ensures { idx << t } + ensures { result = t[idx] } + raises { Invalid_index -> not (idx << t) } + (*proof*) + = if valid idx t.shape then t.value idx else raise Invalid_index + (*qed*) + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/concat/utils.mlw b/safety-related-profile/sonnx/ops/spec/formal/concat/utils.mlw new file mode 100644 index 00000000..9ebff261 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/concat/utils.mlw @@ -0,0 +1,25 @@ +module Same + + val function same (a b : 'a) : 'a + ensures { a = b -> result = a = b } + +end + +module Product + use int.Int + + (** + Notation for product, for `a <= i < b` of `f i` + *) + let rec function product (a b : int) (f : int -> int) : int + (*proof*) + variant { b-a } + = if b <= a then 1 else f a * product (a+1) b f + (*qed*) + + lemma empty: forall a b f. b <= a -> product a b f = 1 + + axiom equal: forall a b f g. + (forall i. a <= i < b -> f i = g i) -> product a b f = product a b g + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/constant/constant.mlw b/safety-related-profile/sonnx/ops/spec/formal/constant/constant.mlw new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/Makefile b/safety-related-profile/sonnx/ops/spec/formal/conv/Makefile new file mode 100644 index 00000000..ec1992a9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'CONV' +# Ce Makefile est exécuté depuis : ops/spec/formal/CONV/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/conv/generated_doc +CODE_DIR = ../../../code/conv/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'Conv2D' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'Conv2D'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "conv2 operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_CONV = -D c -D conv_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_CONV) conv2.COPConv2d + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv.mlw b/safety-related-profile/sonnx/ops/spec/formal/conv/conv.mlw new file mode 100644 index 00000000..63465c14 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv.mlw @@ -0,0 +1,297 @@ +module OPConv2d + use tensor.tensor.Tensor + use tensor.tensor.Range + use int.Int + use real.Real + use tensor.std.List + use tensor.std.Int + use list.List + use list.Length + use list.Nth + use tensor.std.Cfloat + use tensor.std.Cint + use ref.Ref + + + +(* ===== SECTION 1: Helper function for padding (Zero-Padding) ===== *) + +(** A helper function to access the padded input tensor value. *) +(* Goal: Retrieves a value from the input tensor X, accounting for zero-padding. + * If the effective coordinates (x_h, x_w), after removing padding, fall outside the original tensor bounds, it returns 0.0. + * Inputs: + * x: data real - The input tensor data function. + * x_dims: list int - The dimensions of the input tensor X [N, C, H, W]. + * pad_top, pad_left: int - The padding applied to the top/left of the height/width dimensions. + * n, c: int - Batch and input channel indices. + * x_h, x_w: int - The coordinates in the conceptually padded input space (derived from output position, stride, and dilation). + * Outputs: real - The value from the input tensor, or 0.0 if padded. *) + let ghost function padded_value (x : data real) (x_dims : list int) + (pad_top pad_left : int) (n c x_h x_w : int) : real = + requires { length x_dims = 4 } + let padded_h = x_h - pad_top in (* Convert padded index to original index *) + let padded_w = x_w - pad_left in (* Convert padded index to original index *) + let padded_ks = Cons n (Cons c (Cons padded_h (Cons padded_w Nil))) in + if valid padded_ks x_dims then + x padded_ks (* Return value if coordinates are within bounds *) + else + 0.0 (* [T7] *) (* Return 0.0 if coordinates are out of bounds *) + +(* ===== SECTION 2: Data-level Convolution (Weighted Sum) ===== *) + +(** The main 2D convolution data operation. *) +(* Goal: Defines the function that computes the value of a single output pixel Y[n, m, y_h, y_w]. + * This is achieved by iterating over all input channels (c), kernel height (i), and kernel width (j), performing the dot product, and adding the bias. + * Inputs: Tensors (data functions) X, W, B, their dimensions, and all convolution attributes. + * Outputs: data real - The data function for the output tensor Y. *) + let ghost function dconv2d (x : data real) (x_dims : list int) + (w : data real) (w_dims : list int) (b : data real) (b_dims : list int) + (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int) + (str_h str_w : int) : data real + requires { length x_dims = 4 } (*[T1] X shape: [N, C, H, W] *) + requires { length w_dims = 4 } (*[T3] W shape: [M, C, kH, kW] where M=C_out *) + requires { length b_dims = 1 } (* [T4] *) + requires { get_dim x_dims 1 = get_dim w_dims 1 } (*[T13] C_in = C_kernel/group *) + requires { get_dim w_dims 0 = get_dim b_dims 0 } (* [T5] M = M_bias*) + requires { str_h > 0 /\ str_w > 0 } (* [T6] *) + requires { dil_h > 0 /\ dil_w > 0 } (* [T11] *) + = fun yks -> (* Input 'yks' is the output coordinate list: [n, m, y_h, y_w] *) + if length yks <> 4 then 0.0 else + let Cons n (Cons m (Cons y_h (Cons y_w Nil))) = yks in (* Destructure output coordinates *) + let c_in = get_dim w_dims 1 in (* Number of input channels *) + let kh = get_dim w_dims 2 in (* Kernel height *) + let kw = get_dim w_dims 3 in (* Kernel width *) + + let sum_value = ref 0.0 in (* Accumulator for the convolution sum *) + for c = 0 to c_in - 1 do (* Loop over input channels *) + invariant { true } + for i = 0 to kh - 1 do (* Loop over kernel height (i) *) + invariant { true } + for j = 0 to kw - 1 do (* Loop over kernel width (j) *) + invariant { true } + (* Calculate corresponding H/W coordinate in the padded input X *) + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + j * dil_w in + let x_val = padded_value x x_dims pad_top pad_left n c x_h x_w in (* Get padded input value *) + let w_coords = Cons m (Cons c (Cons i (Cons j Nil))) in + let w_val = w w_coords in (* Get kernel weight value *) + sum_value := !sum_value + (x_val * w_val) (* Accumulate the product *) + done + done + done; + let bias_coords = Cons m Nil in + let bias_val = b bias_coords in (* Get the bias value for output channel m *) + !sum_value + bias_val (* Final output value = Weighted Sum + Bias *) + +(* --- *) + +(* ===== SECTION 3: Tensor-level Convolution Operator and Shape Calculation ===== *) +(** The tensor-level 2D convolution operator. *) +(* Goal: Constructs the final output tensor Y, calculating its dimensions and assigning the dconv2d data function. + * Inputs: The three tensors X, W, B, and all convolution attributes. + * Outputs: tensor real - The resulting output tensor Y [N_out, C_out, H_out, W_out]. *) + let ghost function opconv2d (x w b : tensor real) + (pad_top pad_bottom pad_left pad_right : int) (dil_h dil_w : int) + (str_h str_w : int) : tensor real + requires { length x.dims = 4 } (* [T1] [N, C, H, W] *) + requires { length w.dims = 4 } (* [T3] [M, C, kH, kW] *) + requires { length b.dims = 1 } (* [T4] [M] *) + requires { get_dim x.dims 1 = get_dim w.dims 1 } (* [T13] C_in = C_kernel/group *) + requires { get_dim w.dims 0 = get_dim b.dims 0 } (* [T5] M = M_bias *) + requires { str_h > 0 /\ str_w > 0 } (* [T6] *) + requires { dil_h > 0 /\ dil_w > 0 } (* [T11] *) + requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 } (* [T8] *) + ensures { length result.dims = 4 } (* [T12] *) + ensures { get_dim result.dims 0 = get_dim x.dims 0 } (* N_out = N_in *) + ensures { get_dim result.dims 1 = get_dim w.dims 0 } (* C_out = M *) + (* Main ensures clause: the output data must be exactly the function dconv2d *) + ensures {result.data = dconv2d x.data x.dims w.data w.dims b.data b.dims + pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w} + (*proof*) + = let n = get_dim x.dims 0 in + let c_in = get_dim x.dims 1 in + let h_in = get_dim x.dims 2 in + let w_in = get_dim x.dims 3 in + let m_out = get_dim w.dims 0 in + let kh = get_dim w.dims 2 in + let kw = get_dim w.dims 3 in + + (* Calculate Effective Kernel Size (with dilation) *) + let effective_kh = (kh - 1) * dil_h + 1 in + let effective_kw = (kw - 1) * dil_w + 1 in + (* Calculate Output Height (H_out) using the convolution formula: + * H_out = floor((H_in + P_top + P_bottom - effective_kH) / S_h) + 1 *) + let out_h = (h_in + pad_top + pad_bottom - effective_kh) ./ (str_h + 1) in (* [T2] *) + (* Calculate Output Width (W_out) *) + let out_w = (w_in + pad_left + pad_right - effective_kw) ./ (str_w + 1) in (* [T2] *) + let output_dims = Cons n (Cons m_out (Cons out_h (Cons out_w Nil))) in (* [T12] *) + (* Return the final output tensor record *) + { dims = output_dims ; + data = dconv2d x.data x.dims w.data w.dims b.data b.dims + pad_top pad_bottom pad_left pad_right dil_h dil_w str_h str_w ; + + background = 0.0 } + (*qed*) +end + + +module CtensorConv + use int.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use ref.Ref + use OPConv2d + use tensor.libtensor.CTensor +(* ===== SECTION 1: Function Definition and Pre/Postconditions ===== *) + + (* Goal: Implements the imperative 2D convolution operation (Conv2D) by iterating over all output pixels, + * calculating the weighted sum, and storing the result in the 'output' tensor. + * Inputs: + * x, w, b: ctensor - Input, Kernel (weights), and Bias tensors. + * pad_top...str_w: int32 - Convolution attributes (Padding, Dilation, Stride). + * output: ctensor - The tensor where the result will be stored (modified in-place). + * Outputs: unit (returns nothing, modifies 'output' in-place). *) +let ctensor_conv2d (x : ctensor) (w : ctensor) (b : ctensor) + (pad_top pad_bottom pad_left pad_right : int32) + (dil_h dil_w : int32) (str_h str_w : int32) + (output : ctensor): unit = + requires { valid_tensor x } + requires { valid_tensor w } + requires { valid_tensor b } + requires { valid_tensor output } + requires { x.t_rank = 4 } (* [T1] Input tensor [N, C, H, W] *) + requires { w.t_rank = 4 } (* [T3] Kernel tensor [M, C, kH, kW] *) + requires { b.t_rank = 1 } (* [T4] Bias tensor [M] *) + requires { output.t_rank = 4 } (* [T12] Output tensor [N, M, oH, oW] *) + (* Attribute constraints: Stride and Dilation must be positive, Padding must be non-negative. *) + requires { str_h > 0 /\ str_w > 0 } (* [T6] *) + requires { dil_h > 0 /\ dil_w > 0 } (* [T11] *) + requires { pad_top >= 0 /\ pad_bottom >= 0 /\ pad_left >= 0 /\ pad_right >= 0 } (* [T8] *) + (* Postcondition: The concrete tensor 'output' must be equal to the ghost specification of Conv2D. *) + ensures { tensor output = OPConv2d.opconv2d (tensor x) (tensor w) (tensor b) + (Int32.to_int pad_top) (Int32.to_int pad_bottom) + (Int32.to_int pad_left) (Int32.to_int pad_right) + (Int32.to_int dil_h) (Int32.to_int dil_w) + (Int32.to_int str_h) (Int32.to_int str_w) } + (*proof*) + + (* ===== SECTION 2: Dimension Extraction and Variable Initialization ===== *) + + (* Extraction des dimensions de chaque tenseur pour les limites des boucles *) + let n_batches = x.t_dims[0] in (* Batch size (N) *) + let c_in = x.t_dims[1] in (* Input Channels (C_in) *) + let h_in = x.t_dims[2] in (* Input Height (H_in) *) + let w_in = x.t_dims[3] in (* Input Width (W_in) *) + let m_out = w.t_dims[0] in (* Output Channels / Kernel count (M) *) + let kh = w.t_dims[2] in (* Kernel Height (kH) *) + let kw = w.t_dims[3] in (* Kernel Width (kW) *) + + let h_out = output.t_dims[2] in (* Output Height (oH) *) + let w_out = output.t_dims[3] in (* Output Width (oW) *) + + + (* Conversion des paramètres de convolution en variables locales (int32 -> int32) *) + let pad_top_i = pad_top in + let pad_left_i = pad_left in + let dil_h_i = dil_h in + let dil_w_i = dil_w in + let str_h_i = str_h in + let str_w_i = str_w in + + + (* Allocation de tableaux temporaires pour stocker les coordonnées 4D (X, W, Output) ou 1D (B) + * Ces tableaux sont utilisés par la fonction 'coffset' pour calculer l'indice 1D dans t_data. *) + let x_coords = malloc_int32 (to_uint32 4) in + let w_coords = malloc_int32 (to_uint32 4) in + let b_coords = malloc_int32 (to_uint32 1) in + let output_coords = malloc_int32 (to_uint32 4) in + + if is_not_null x_coords && is_not_null w_coords && is_not_null b_coords && is_not_null output_coords then begin + (* Boucles imbriquées pour calculer la convolution *) + (* ===== SECTION 3: Core Iteration Loops (Output Tensor) ===== *) + for n = 0 to n_batches - 1 do (* Loop over Batch dimension (N) *) + invariant { true } + for m = 0 to m_out - 1 do (* Loop over Output Channels (M) *) + invariant { true } + for oh = 0 to h_out - 1 do (* Loop over Output Height (oH) *) + invariant { true } + for ow = 0 to w_out - 1 do (* Loop over Output Width (oW) *) + invariant { true } + + (* Initialisation de l'accumulateur pour le produit scalaire (dot product) *) + let conv_sum = ref (f64 0.0) in + + (* ===== SECTION 4: Convolution and Summation Loops (Kernel) ===== *) + + for c = 0 to c_in - 1 do (* Loop over Input Channels (C) *) + invariant { true } + for k_h = 0 to kh - 1 do (* Loop over Kernel Height (kH) *) + invariant { true } + for k_w = 0 to kw - 1 do (* Loop over Kernel Width (kW) *) + invariant { true } + + (* 1. Calculate the indices in the input space *with padding* (ih, iw) *) + let ih = oh * str_h_i + k_h * dil_h_i in + let iw = ow * str_w_i + k_w * dil_w_i in + + (* 2. Convert to *original* input indices (after subtracting padding) *) + let padded_ih = ih - pad_top_i in + let padded_iw = iw - pad_left_i in + (* 3. Retrieve the input value (X) with zero-padding handling *) + let x_val = + if 0 <= padded_ih && padded_ih < h_in && 0 <= padded_iw && padded_iw < w_in then begin + (* Calculate the X offset if the index is within the original bounds *) + set_ofs x_coords 0 n; + set_ofs x_coords 1 c; + set_ofs x_coords 2 padded_ih; + set_ofs x_coords 3 padded_iw; + let x_offset = coffset x_coords x.t_dims 4 in + if x_offset >= 0 then x.t_data[x_offset] else f64 0.0 (* Value of X *) + end else f64 0.0 (* Value 0.0 for padding *) + in + + (* 4. Retrieve the kernel value (W) and calculate its offset *) + set_ofs w_coords 0 m; + set_ofs w_coords 1 c; + set_ofs w_coords 2 k_h; + set_ofs w_coords 3 k_w; + let w_offset = coffset w_coords w.t_dims 4 in + let w_val = if w_offset >= 0 then w.t_data[w_offset] else f64 0.0 in + + (* 5. Accumulate the product X_val * W_val into the convolution sum *) + conv_sum := !conv_sum .+ (x_val .* w_val) + done + done + done; + + (* ===== SECTION 5: Final Result Calculation and Assignment ===== *) + + (* 6. Add the bias (B) (corresponding to the output channel m) *) + set_ofs b_coords 0 m; + let b_offset = coffset b_coords b.t_dims 1 in + let bias_val = if b_offset >= 0 then b.t_data[b_offset] else f64 0.0 in + let final_val = !conv_sum .+ bias_val in + + (* 7. Calculate the final offset and store the result in the 'output' data array *) + set_ofs output_coords 0 n; + set_ofs output_coords 1 m; + set_ofs output_coords 2 oh; + set_ofs output_coords 3 ow; + let output_offset = coffset output_coords output.t_dims 4 in + if output_offset >= 0 then output.t_data[output_offset] <- final_val + done + done + done + done + end + + + +(*qed*) +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv2.mlw b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2.mlw new file mode 100644 index 00000000..8a3cdf8c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2.mlw @@ -0,0 +1,729 @@ +module OPConv2d + + use tensor.Tensor + use real.Real + use int.Int + use std.Int + use list.List + use int.EuclideanDivision + use tensor.Range + use list.Length + + function convDims (x w :tensor real) (str_h str_w :int) + (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : list int + = + let dY_0 = get_dim x.dims 0 in + let dY_1 = get_dim w.dims 0 in + + let alpha = get_dim x.dims 2 + pad_top + pad_bottom in + let theta = dir_h * (get_dim w.dims 2 - 1) + 1 in + let dY_2 = div (alpha - theta) str_h + 1 in + + let beta = get_dim x.dims 3 + pad_left + pad_right in + let gamma = dir_w * (get_dim w.dims 3 - 1) + 1 in + let dY_3 = div (beta - gamma) str_w + 1 in + + Cons dY_0 (Cons dY_1 (Cons dY_2 (Cons dY_3 Nil))) + + + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 + + let rec ghost function w_cools_calculate (x w : tensor real) (c: int) (i:int) (j:int) (jj:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { j - jj } + = + if jj >= j then + 0.0 + else + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons jj Nil)))) in + Real.(x_val * w_val + w_cools_calculate x w c i j Int.(jj + 1) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + + let rec lemma w_cools_split (x w : tensor real) (c i j jj k n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { jj <= k < j } + variant { k - jj } + ensures { + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + k * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons k Nil)))) in + w_cools_calculate x w c i Int.(k+1) jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(x_val * w_val + w_cools_calculate x w c i k jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if jj < k then w_cools_split x w c i j Int.(jj+1) k n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_lines_calculate (x w : tensor real) (c: int) (i:int) (ii:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { i - ii } + = + if ii >= i then + 0.0 + else + w_cools_calculate x w c ii j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c i Int.(ii + 1) j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_lines_split (x w : tensor real) (c i ii k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { ii <= k < i } + variant { k - ii } + ensures { + w_lines_calculate x w c Int.(k+1) ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_cools_calculate x w c k j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c k ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if ii < k then w_lines_split x w c i Int.(ii+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_channels_calculate (x w :tensor real) (c:int )(cc:int) (i:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { c - cc } + = + if cc >= c then + 0.0 + else + w_lines_calculate x w cc i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w c Int.(cc + 1) i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_channels_split (x w : tensor real) (c i cc k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { cc <= k < c } + variant { k - cc } + ensures { + w_channels_calculate x w Int.(k+1) cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_lines_calculate x w k i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w k cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if cc < k then w_channels_split x w c i Int.(cc+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + function dconv (x w : tensor real) (y_dims : list int) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left :int) : data real + = + let c = get_dim w.dims 1 in + let i = get_dim w.dims 2 in + let j = get_dim w.dims 3 in + fun ks -> + if valid ks y_dims then + let n = get_dim ks 0 in + let m = get_dim ks 1 in + let y_h = get_dim ks 2 in + let y_w = get_dim ks 3 in + w_channels_calculate x w c 0 i j n m y_h y_w str_h str_w dir_h dir_w pad_top pad_left + else + x.background + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons _ ds -> get_dim_positive ds end + + let ghost function conv (x w : tensor real) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : tensor real + requires { str_h > 0 /\ str_w > 0 } + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + requires { dir_h > 0 /\ dir_w > 0 } + requires { length x.dims = 4 } + requires { get_dim x.dims 1 = get_dim w.dims 1 } + requires { length w.dims = 4 } + requires { let y_shape = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + ensures { result.background = x.background } + ensures { result.dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right } + ensures { let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + result.data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left + } + = + let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + { dims = y_dims; + data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left ; + background = x.background; + } +end + + +module COPConv2d + + use OPConv2d + use tensor.Tensor + use tensor.Range + use real.Real + use int.Int + use list.List + use layout.CFlat + + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + use std.Int + use list.Length + use layout.CFlat + use tensor.Range + + + let lemma ivector_get_dim_value_at (coords: iarray) (n: int) (i: int) + requires { n = 4 } + requires { 0 <= i < n } + requires { valid_range coords 0 n } + ensures { get_dim (ivector coords n) i = value_at coords i } + = + assert { ivector coords 4 = Cons (to_int (value_at coords 0)) + (Cons (to_int (value_at coords 1)) + (Cons (to_int (value_at coords 2)) + (Cons (to_int (value_at coords 3)) Nil))) }; + assert { i = 0 \/ i = 1 \/ i = 2 \/ i = 3 } + + + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + let w_cools_calculate (x w: ctensor) (c: int32) (i:int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + requires { 0 <= i < value_at w.t_dims 2 } + + requires { in_bounds (i * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { in_bounds (y_h * str_h + i * dil_h) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + requires { let x_h = y_h * str_h + i * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + ensures { let (value, flag) = result in + flag -> w_cools_calculate (tensor x) (tensor w) c i (value_at w.t_dims 3) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let ref flag = False in + let ref sum = (f32 0.0) in + let x_coords_array = malloc (to_uint32 4) in + let w_coords_array = malloc (to_uint32 4) in + let cols = w.t_dims[3] in + if is_not_null x_coords_array && is_not_null w_coords_array then begin + flag <- True; + for jj = 0 to cols - 1 do + invariant { sum = w_cools_calculate (tensor x) (tensor w) c i jj 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + x_coords_array[0] <- n; + x_coords_array[1] <- c; + x_coords_array[2] <- x_h; + x_coords_array[3] <- x_w; + assert { ivector x_coords_array 4 = Cons (to_int n) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) } ; + let (x_val, aux_flag) = coords_from_X_p x x_coords_array pad_top pad_left in + assert { aux_flag -> x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + if aux_flag then begin + w_coords_array[0] <- m; + w_coords_array[1] <- c; + w_coords_array[2] <- i; + w_coords_array[3] <- jj; + + ghost + begin + valid_bounds_2 w_coords_array w.t_dims 0 4; + assert { Range.valid (ivector w_coords_array w.t_rank) (ivector w.t_dims w.t_rank) }; + w_cools_split (tensor x) (tensor w) (to_int c) (to_int i) (to_int cols) 0 (to_int jj) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + + + let w_coords = coffset w_coords_array w.t_dims w.t_rank in + let w_val = w.t_data[w_coords] in + assert {Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil))) = ivector w_coords_array w.t_rank } ; + assert { (tensor w).Tensor.data (Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil)))) = w_val } ; + assert { x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + sum <- sum .+ (x_val .* w_val); + assert { sum = w_cools_calculate (tensor x) (tensor w) c i Int.(jj+1) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + end + else begin + flag <- False; + (f32 0.0, flag) + end + + let w_lines_calculate (x w : ctensor) (c: int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_lines_calculate (tensor x) (tensor w) c (value_at w.t_dims 2) 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let i = w.t_dims[2] in + let ref flag = False in + let ref sum = (f32 0.0) in + for ii = 0 to i - 1 do + invariant { sum = w_lines_calculate (tensor x) (tensor w) c ii 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let (value, aux_flag) = w_cools_calculate x w c ii n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + ghost + begin + w_lines_split (tensor x) (tensor w) (to_int c) (to_int i) 0 (to_int ii) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let w_channels_calculate (x w : ctensor) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_channels_calculate (tensor x) (tensor w) (to_int(value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) = value + } + = + let c = w.t_dims[1] in + let ref flag = False in + let ref sum = (f32 0.0) in + for cc = 0 to c - 1 do + invariant { sum = w_channels_calculate (tensor x) (tensor w) cc 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) } + let (value, aux_flag) = w_lines_calculate x w cc n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let cconv (x w r : ctensor) (str_h str_w : int32) (dil_h dil_w : int32) + (pad_top pad_left pad_bottom pad_right : int32) : bool + + requires { valid_tensor x} + requires { valid_tensor w} + requires { valid_tensor r} + requires { x.t_rank = w.t_rank = r.t_rank = 4 } + requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right } + requires { str_h > 0 /\ str_w > 0 } + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + requires { dil_h > 0 /\ dil_w > 0 } + requires { length (tensor x).dims = 4 } + requires { get_dim (tensor x).dims 1 = get_dim (tensor w).dims 1 } + requires { length (tensor w).dims = 4 } + requires { let y_shape = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { forall y_h: int. 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h) } + requires { forall ii y_h:int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { forall y_w: int. 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w) } + requires { forall jj y_w:int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii y_h: int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj y_w: int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + requires { forall mm:int. 0 <= mm < value_at r.t_dims 1 -> mm < value_at w.t_dims 0 } + + ensures { result -> tensor r = conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } + + = + let n = r.t_dims[0] in + let m = r.t_dims[1] in + let y_h = r.t_dims[2] in + let y_w = r.t_dims[3] in + let r_coords_array = malloc (to_uint32 4) in + if is_not_null r_coords_array then begin + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + + assert { ivector r_coords_array r.t_rank = Cons ( nn) (Cons ( mm) (Cons ( y_hh) (Cons ( y_ww) Nil))) } ; + assert { value = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left } ; + assert { let r_coords = CFlat.offset (ivector r_coords_array r.t_rank) (ivector r.t_dims r.t_rank) in + value_at r.t_data r_coords = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left }; + + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; + + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; + + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + ) + }; + + assert { forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + )} + end + else + return false + done; + done; + done; + done; + assert { tensor r == conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } ; + true + end + else + false + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml new file mode 100644 index 00000000..c737847d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml @@ -0,0 +1,952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml.bak new file mode 100644 index 00000000..c737847d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3session.xml.bak @@ -0,0 +1,952 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz new file mode 100644 index 00000000..9e72a37c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz.bak new file mode 100644 index 00000000..9e72a37c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/conv/conv2/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/conv/conv_driver.drv new file mode 100644 index 00000000..40dd1dce --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module conv2.COPConv2d + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/conv/conv_static.mlw b/safety-related-profile/sonnx/ops/spec/formal/conv/conv_static.mlw new file mode 100644 index 00000000..e80b0b6f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/conv/conv_static.mlw @@ -0,0 +1,726 @@ +module OPConv2d + + use tensor.tensor.Tensor + use real.Real + use int.Int + use tensor.std.Int + use list.List + use int.EuclideanDivision + use tensor.tensor.Range + use list.Length + + function convDims (x w :tensor real) (str_h str_w :int) + (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : list int + = + let dY_0 = get_dim x.dims 0 in + let dY_1 = get_dim w.dims 0 in + + let alpha = get_dim x.dims 2 + pad_top + pad_bottom in + let theta = dir_h * (get_dim w.dims 2 - 1) + 1 in + let dY_2 = div (alpha - theta) str_h + 1 in + + let beta = get_dim x.dims 3 + pad_left + pad_right in + let gamma = dir_w * (get_dim w.dims 3 - 1) + 1 in + let dY_3 = div (beta - gamma) str_w + 1 in + + Cons dY_0 (Cons dY_1 (Cons dY_2 (Cons dY_3 Nil))) + + + function coords_from_X_p (x: tensor real) (x_p_coords : list int) ( pad_top pad_left :int) : real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + 0.0 + + let rec ghost function w_cools_calculate (x w : tensor real) (c: int) (i:int) (j:int) (jj:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { j - jj } + = + if jj >= j then + 0.0 + else + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons jj Nil)))) in + Real.(x_val * w_val + w_cools_calculate x w c i j Int.(jj + 1) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + + let rec lemma w_cools_split (x w : tensor real) (c i j jj k n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { jj <= k < j } + variant { k - jj } + ensures { + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + k * dil_w in + let x_val = coords_from_X_p x (Cons n (Cons c (Cons x_h (Cons x_w Nil))) ) pad_top pad_left in + let w_val = w.data (Cons m (Cons c (Cons i (Cons k Nil)))) in + w_cools_calculate x w c i Int.(k+1) jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(x_val * w_val + w_cools_calculate x w c i k jj n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if jj < k then w_cools_split x w c i j Int.(jj+1) k n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_lines_calculate (x w : tensor real) (c: int) (i:int) (ii:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { i - ii } + = + if ii >= i then + 0.0 + else + w_cools_calculate x w c ii j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c i Int.(ii + 1) j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_lines_split (x w : tensor real) (c i ii k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { ii <= k < i } + variant { k - ii } + ensures { + w_lines_calculate x w c Int.(k+1) ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_cools_calculate x w c k j 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_lines_calculate x w c k ii j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if ii < k then w_lines_split x w c i Int.(ii+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + let rec ghost function w_channels_calculate (x w :tensor real) (c:int )(cc:int) (i:int) (j:int) (n m y_h y_w: int) (str_h str_w :int) (dil_h dil_w :int) (pad_top pad_left :int) : real + variant { c - cc } + = + if cc >= c then + 0.0 + else + w_lines_calculate x w cc i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w c Int.(cc + 1) i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + + let rec lemma w_channels_split (x w : tensor real) (c i cc k j n m y_h y_w : int) (str_h str_w dil_h dil_w pad_top pad_left : int) + requires { cc <= k < c } + variant { k - cc } + ensures { + w_channels_calculate x w Int.(k+1) cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = + Real.(w_lines_calculate x w k i 0 j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left + w_channels_calculate x w k cc i j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left) + } + = + if cc < k then w_channels_split x w c i Int.(cc+1) k j n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left else () + + function dconv (x w : tensor real) (y_dims : list int) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left :int) : data real + = + let c = get_dim w.dims 1 in + let i = get_dim w.dims 2 in + let j = get_dim w.dims 3 in + fun ks -> + if valid ks y_dims then + let n = get_dim ks 0 in + let m = get_dim ks 1 in + let y_h = get_dim ks 2 in + let y_w = get_dim ks 3 in + w_channels_calculate x w c 0 i j n m y_h y_w str_h str_w dir_h dir_w pad_top pad_left + else + x.background + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons _ ds -> get_dim_positive ds end + + let ghost function conv (x w : tensor real) (str_h str_w :int) (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : tensor real + requires { str_h > 0 /\ str_w > 0 } + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + requires { dir_h > 0 /\ dir_w > 0 } + requires { length x.dims = 4 } + requires { get_dim x.dims 1 = get_dim w.dims 1 } + requires { length w.dims = 4 } + requires { let y_shape = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + ensures { result.background = x.background } + ensures { result.dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right } + ensures { let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + result.data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left + } + = + let y_dims = convDims x w str_h str_w dir_h dir_w pad_top pad_left pad_bottom pad_right in + { dims = y_dims; + data = dconv x w y_dims str_h str_w dir_h dir_w pad_top pad_left ; + background = x.background; + } +end + + +module COPConv2d + + use OPConv2d + use tensor.tensor.Tensor + use tensor.tensor.Range + use real.Real + use int.Int + use list.List + use tensor.layout.CFlat + + use tensor.libtensor.CTensor + use tensor.libvector.CIndex + use tensor.std.Clib + use mach.int.Int32 + use tensor.std.Cfloat + use tensor.std.Int + use list.Length + use tensor.layout.CFlat + use tensor.tensor.Range + + + let lemma ivector_get_dim_value_at (coords: iarray) (n: int) (i: int) + requires { n = 4 } + requires { 0 <= i < n } + requires { valid_range coords 0 n } + ensures { get_dim (ivector coords n) i = value_at coords i } + = + assert { ivector coords 4 = Cons (to_int (value_at coords 0)) + (Cons (to_int (value_at coords 1)) + (Cons (to_int (value_at coords 2)) + (Cons (to_int (value_at coords 3)) Nil))) }; + assert { i = 0 \/ i = 1 \/ i = 2 \/ i = 3 } + + + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left :int32) : (float, bool) = + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = (x_p_coords[2]) - pad_top in + let m = (x_p_coords[3]) - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + flag <- True; + x_coords_array[0] <- b; + x_coords_array[1] <- c; + x_coords_array[2] <- n; + x_coords_array[3] <- m; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + ((f32 0.0), flag) + end + end + else begin + flag <- False; + ((f32 0.0), flag) + end + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + let w_cools_calculate (x w: ctensor) (c: int32) (i:int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + requires { 0 <= i < value_at w.t_dims 2 } + + requires { in_bounds (i * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { in_bounds (y_h * str_h + i * dil_h) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + requires { let x_h = y_h * str_h + i * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + ensures { let (value, flag) = result in + flag -> w_cools_calculate (tensor x) (tensor w) c i (value_at w.t_dims 3) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let ref flag = False in + let ref sum = (f32 0.0) in + let x_coords_array = malloc (to_uint32 4) in + let w_coords_array = malloc (to_uint32 4) in + let cols = w.t_dims[3] in + if is_not_null x_coords_array && is_not_null w_coords_array then begin + flag <- True; + for jj = 0 to cols - 1 do + invariant { sum = w_cools_calculate (tensor x) (tensor w) c i jj 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let x_h = y_h * str_h + i * dil_h in + let x_w = y_w * str_w + jj * dil_w in + x_coords_array[0] <- n; + x_coords_array[1] <- c; + x_coords_array[2] <- x_h; + x_coords_array[3] <- x_w; + assert { ivector x_coords_array 4 = Cons (to_int n) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) } ; + let (x_val, aux_flag) = coords_from_X_p x x_coords_array pad_top pad_left in + assert { aux_flag -> x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + if aux_flag then begin + w_coords_array[0] <- m; + w_coords_array[1] <- c; + w_coords_array[2] <- i; + w_coords_array[3] <- jj; + + ghost + begin + valid_bounds_2 w_coords_array w.t_dims 0 4; + assert { Range.valid (ivector w_coords_array w.t_rank) (ivector w.t_dims w.t_rank) }; + w_cools_split (tensor x) (tensor w) (to_int c) (to_int i) (to_int cols) 0 (to_int jj) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + + + let w_coords = coffset w_coords_array w.t_dims w.t_rank in + let w_val = w.t_data[w_coords] in + assert {Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil))) = ivector w_coords_array w.t_rank } ; + assert { (tensor w).Tensor.data (Cons (to_int m) (Cons (to_int c) (Cons (to_int i) (Cons (jj) Nil)))) = w_val } ; + assert { x_val = coords_from_X_p (tensor x) (ivector x_coords_array 4) (to_int pad_top) (to_int pad_left) } ; + sum <- sum .+ (x_val .* w_val); + assert { sum = w_cools_calculate (tensor x) (tensor w) c i Int.(jj+1) 0 n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + end + else begin + flag <- False; + (f32 0.0, flag) + end + + let w_lines_calculate (x w : ctensor) (c: int32) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + requires { 0 <= c < value_at w.t_dims 1 } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_lines_calculate (tensor x) (tensor w) c (value_at w.t_dims 2) 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left = value + } + = + let i = w.t_dims[2] in + let ref flag = False in + let ref sum = (f32 0.0) in + for ii = 0 to i - 1 do + invariant { sum = w_lines_calculate (tensor x) (tensor w) c ii 0 (value_at w.t_dims 3) n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left } + let (value, aux_flag) = w_cools_calculate x w c ii n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + ghost + begin + w_lines_split (tensor x) (tensor w) (to_int c) (to_int i) 0 (to_int ii) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + end; + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let w_channels_calculate (x w : ctensor) (n m y_h y_w: int32) (str_h str_w :int32) (dil_h dil_w :int32) (pad_top pad_left :int32) : (float, bool) + requires { valid_tensor x } + requires { valid_tensor w } + requires { x.t_rank = w.t_rank = 4 } + requires { 0 <= m < value_at w.t_dims 0 } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { in_bounds (y_h * str_h) } + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { in_bounds (y_w * str_w) } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii: int. 0 <= ii < value_at w.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + ensures { let (value, flag) = result in + flag -> w_channels_calculate (tensor x) (tensor w) (to_int(value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) = value + } + = + let c = w.t_dims[1] in + let ref flag = False in + let ref sum = (f32 0.0) in + for cc = 0 to c - 1 do + invariant { sum = w_channels_calculate (tensor x) (tensor w) cc 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) (to_int n) (to_int m) (to_int y_h) (to_int y_w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) } + let (value, aux_flag) = w_lines_calculate x w cc n m y_h y_w str_h str_w dil_h dil_w pad_top pad_left in + if aux_flag then begin + flag <- True; + sum <- sum .+ value; + end + else begin + flag <- False; + return (f32 0.0, flag) + end + done; + (sum, flag) + + +let cconv (x w r : ctensor) (str_h str_w : int32) (dil_h dil_w : int32) + (pad_top pad_left pad_bottom pad_right : int32) (r_coords_array : iarray) : bool + + requires { valid_tensor x} + requires { valid_tensor w} + requires { valid_tensor r} + requires { x.t_rank = w.t_rank = r.t_rank = 4 } + requires { valid_range r_coords_array 0 4 } + requires { writable r_coords_array } + requires { ivector r.t_dims r.t_rank = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right } + requires { str_h > 0 /\ str_w > 0 } + requires { pad_top >= 0 /\ pad_left >= 0 /\ pad_bottom >= 0 /\ pad_right >= 0 } + requires { dil_h > 0 /\ dil_w > 0 } + requires { length (tensor x).dims = 4 } + requires { get_dim (tensor x).dims 1 = get_dim (tensor w).dims 1 } + requires { length (tensor w).dims = 4 } + requires { let y_shape = convDims (tensor x) (tensor w) str_h str_w dil_h dil_w pad_top pad_left pad_bottom pad_right in + get_dim y_shape 2 > 0 /\ get_dim y_shape 3 > 0 + } + + requires { forall ii:int. 0 <= ii < value_at w.t_dims 2 -> in_bounds (ii * dil_h) } + requires { forall y_h: int. 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h) } + requires { forall ii y_h:int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> in_bounds (y_h * str_h + ii * dil_h) } + + requires { forall jj. 0 <= jj < value_at w.t_dims 3 -> in_bounds (jj * dil_w) } + requires { forall y_w: int. 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w) } + requires { forall jj y_w:int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> in_bounds (y_w * str_w + jj * dil_w) } + + requires { forall ii y_h: int. 0 <= ii < value_at w.t_dims 2 /\ 0 <= y_h < value_at r.t_dims 2 -> + let x_h = y_h * str_h + ii * dil_h in + in_bounds (x_h - pad_top) + } + requires { forall jj y_w: int. 0 <= jj < value_at w.t_dims 3 /\ 0 <= y_w < value_at r.t_dims 3 -> + let x_w = y_w * str_w + jj * dil_w in + in_bounds (x_w - pad_left) + } + + requires { forall mm:int. 0 <= mm < value_at r.t_dims 1 -> mm < value_at w.t_dims 0 } + + ensures { result -> tensor r = conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } + + = + let n = r.t_dims[0] in + let m = r.t_dims[1] in + let y_h = r.t_dims[2] in + let y_w = r.t_dims[3] in + for nn = 0 to n - 1 do + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + r_coords_array[0] <- nn; + for mm = 0 to m - 1 do + invariant { value_at r_coords_array 0 = nn } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + r_coords_array[1] <- mm; + for y_hh = 0 to y_h - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + + } + r_coords_array[2] <- y_hh; + for y_ww = 0 to y_w - 1 do + invariant { value_at r_coords_array 0 = nn /\ value_at r_coords_array 1 = mm /\ value_at r_coords_array 2 = y_hh } + invariant { + forall nn_: int. 0 <= nn_ < nn -> + (forall mm: int. 0 <= mm < m -> + (forall yy_hh: int. 0 <= yy_hh < y_h -> + (forall yy_ww: int. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn_ mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm_ yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + ) + } + invariant { + forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm yy_hh yy_ww str_h str_w dil_h dil_w pad_top pad_left + ) + } + invariant { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + (value_at r.t_data off) = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) ( nn) ( mm) ( y_hh) yy_ww ( str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) + } + r_coords_array[3] <- y_ww; + let (value, flag) = w_channels_calculate x w nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left in + if flag then begin + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + r.t_data[r_coords] <- value; + + assert { ivector r_coords_array r.t_rank = Cons ( nn) (Cons ( mm) (Cons ( y_hh) (Cons ( y_ww) Nil))) } ; + assert { value = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left } ; + assert { let r_coords = CFlat.offset (ivector r_coords_array r.t_rank) (ivector r.t_dims r.t_rank) in + value_at r.t_data r_coords = w_channels_calculate (tensor x) (tensor w) (to_int (value_at w.t_dims 1)) 0 (to_int(value_at w.t_dims 2)) (to_int(value_at w.t_dims 3)) nn mm y_hh y_ww str_h str_w dil_h dil_w pad_top pad_left }; + + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall yy_ww. 0 <= yy_ww < y_ww -> + let coords = Cons (nn) (Cons (mm) (Cons (y_hh) (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; + + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + + assert { forall yy_hh. 0 <= yy_hh < y_hh -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; + + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + ) + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + ) + }; + + assert { forall nn_. 0 <= nn_ < nn -> + (forall mm_. 0 <= mm_ < m -> + (forall yy_hh. 0 <= yy_hh < y_h -> + (forall yy_ww. 0 <= yy_ww < y_w -> + let coords = Cons (nn_) (Cons (mm_) (Cons yy_hh (Cons yy_ww Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + )} + end + else + return false + done; + done; + done; + done; + assert { tensor r == conv (tensor x) (tensor w) (to_int str_h) (to_int str_w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) } ; + true + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/div/div.mlw b/safety-related-profile/sonnx/ops/spec/formal/div/div.mlw new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/exp/exp.mlw b/safety-related-profile/sonnx/ops/spec/formal/exp/exp.mlw new file mode 100644 index 00000000..cb23d910 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/exp/exp.mlw @@ -0,0 +1,18 @@ +(** + Specification of Exp operation on tensors. + *) +module Exp + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + use real.Real + use real.Exp + let function exp (a : tensor real) : tensor real = + ensures { forall i. result.value[i] = exp a.value[i] } + { + shape = a.shape ; + value = fun i -> exp a.value[i] ; + } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/Makefile b/safety-related-profile/sonnx/ops/spec/formal/flatten/Makefile new file mode 100644 index 00000000..af97119c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/flatten/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'flatten' +# Ce Makefile est exécuté depuis : ops/spec/formal/flatten/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/flatten/generated_doc +CODE_DIR = ../../../code/flatten/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'flatten' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'flatten'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "flatten operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_FLATTEN = -D c -D flatten_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_FLATTEN) flatten.COPFlatten + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten.mlw b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten.mlw new file mode 100644 index 00000000..a2ef6b7c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten.mlw @@ -0,0 +1,148 @@ +module OPFlatten + + use tensor.Tensor + use tensor.Range + use layout.CFlat + use int.Int + use real.Real + use std.Int + use list.List + use list.Length + + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons d ds -> get_dim_positive ds end + + + let rec ghost function calculate_dY0 (x : tensor 'a) (axis : int) (i : int) : int + (*Need this requires because of get_dim requires*) + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + variant { axis - i } + = + if Int.(i >= axis) then + 1 + else + get_dim x.dims i * calculate_dY0 x axis Int.(i + 1) + + + let rec lemma calculate_dY0_positive (x: tensor 'a) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { 0 <= i <= axis } + ensures { 0 < calculate_dY0 x axis i } + variant { axis - i } + = + if i >= axis then + () + else + let d = get_dim x.dims i in + calculate_dY0_positive x axis (i + 1) + + let rec ghost function calculate_dY1 (x : tensor 'a) (axis : int) (i : int) : int + requires { 0 <= axis <= length x.dims } + requires { axis <= i <= length x.dims } + variant { length x.dims - i } + = + if Int.(i >= length x.dims) then + 1 + else + get_dim x.dims i * calculate_dY1 x axis Int.(i + 1) + + let rec lemma calculate_dY1_positive (x: tensor 'a) (axis: int) (i: int) + requires { positive x.dims } + requires { 0 <= axis <= length x.dims } + requires { axis <= i <= length x.dims } + ensures { 0 < calculate_dY1 x axis i } + variant { length x.dims - i } + = + if i >= length x.dims then + () + else + let d = get_dim x.dims i in + assert { 0 <= i < length x.dims }; + calculate_dY1_positive x axis (i + 1) + + function calculate_dims (x : tensor 'a) (axis : int) : list int + = + let dY0 = calculate_dY0 x axis 0 in + let dY1 = calculate_dY1 x axis axis in + Cons dY0 (Cons dY1 Nil) + + + function dflatten (x : tensor 'a) (y_shape: list int) : data 'a + = + fun ks -> + if valid ks y_shape then + let y_coords_flat = offset ks y_shape in + let x_coords = index y_coords_flat x.dims in + x.data x_coords + else + x.background + + function normalize_axis (axis : int) (x_rank : int) : int + = + if axis < 0 then + axis + x_rank + else + axis + + let ghost function flatten (x : tensor 'a) (axis : int) : tensor 'a + requires { -length x.dims <= axis <= length x.dims } + + ensures { let axis_normalized = normalize_axis axis (length x.dims) in + result.dims = calculate_dims x axis_normalized } + + ensures { result.background = x.background } + + ensures { let axis_normalized = normalize_axis axis (length x.dims) in + result.data = dflatten x (calculate_dims x axis_normalized) } + = + let axis_normalized = normalize_axis axis (length x.dims) in + let y_shape = calculate_dims x axis_normalized in + { dims = y_shape ; data = dflatten x y_shape ; background = x.background } + +end + + +module COPFlatten + + use OPFlatten + use tensor.Tensor + use list.List + use list.Length + use int.Int + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + + + let flatten (x r : ctensor) (axis: int32) + requires { valid_tensor x } + requires { valid_tensor r } + requires { r.t_rank = 2 } + requires { let axis_normalized = normalize_axis (to_int axis) (length (tensor x).dims) in + (ivector r.t_dims r.t_rank) = calculate_dims (tensor x) axis_normalized } + requires { vdim x.t_dims x.t_rank = vdim r.t_dims r.t_rank } + requires { -length (tensor x).dims <= (to_int axis) <= length (tensor x).dims } + ensures { tensor r = flatten (tensor x) (to_int axis) } + = + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.t_data k = value_at x.t_data k } + r.t_data[i] <- x.t_data[i] + done; + assert { tensor r == flatten (tensor x) (to_int axis) } + + +end + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml new file mode 100644 index 00000000..82b61d26 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml.bak new file mode 100644 index 00000000..82b61d26 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3session.xml.bak @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz new file mode 100644 index 00000000..25dc9178 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz.bak new file mode 100644 index 00000000..25dc9178 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten_driver.drv new file mode 100644 index 00000000..d925b31a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/flatten/flatten_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module flatten.COPFlatten + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/gemm/gemm.mlw b/safety-related-profile/sonnx/ops/spec/formal/gemm/gemm.mlw new file mode 100644 index 00000000..3f2f7e58 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/gemm/gemm.mlw @@ -0,0 +1,25 @@ +(** + Specification of Gemm operation on tensors with real numbers. + *) +module GemmReal + use int.Int + use map.Map + use tensor.Shape + use tensor.Tensor + use real.Real + (** Define the matrix multiplication function *) + let function matmul (A : tensor real) (B : tensor real) (i : int) (j : int) : real = + requires { A.shape[1] = B.shape[0] } (** Ensure matrix multiplication dimensions are consistent *) + ensures { result = sum(k: 0 to A.shape[1]-1) (A.value[i,A.shape[1]-i+k] * B.value[B.shape[0]-k+j,j]) } + { + sum(k: 0 to A.shape[1]-1) (A.value[i,k] * B.value[k,j]) + } + (** Define the gemm operation on tensors **) + let function gemm_tensor (A : tensor real) (B : tensor real) (C : tensor real) : tensor real = + requires { A.shape[1] = B.shape[0] /\ A.shape[0] = C.shape[0] /\ B.shape[1] = C.shape[1] } + ensures { forall i j. result.value[i,j] = matmul(A, B, i, j) + C.value[i,j] } + { + shape = [A.shape[0], B.shape[1]] ; + value = fun i j -> matmul(A, B, i, j) + C.value[i,j] ; + } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/Makefile b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/Makefile new file mode 100644 index 00000000..e0d60bf1 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'leakyrelu' +# Ce Makefile est exécuté depuis : ops/spec/formal/leakyrelu/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/leakyrelu/generated_doc +CODE_DIR = ../../../code/leakyrelu/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'leakyrelu' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'leakyrelu'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "leakyrelu operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_LEAKYRELU = -D c -D leakyrelu_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_LEAKYRELU) leakyrelu.CtensorLeakyRelu + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu.mlw b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu.mlw new file mode 100644 index 00000000..b49291d5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu.mlw @@ -0,0 +1,106 @@ +(* Abstract level *) +module OPLeakyRelu + use tensor.tensor.Tensor + use real.Real + use tensor.std.Cfloat + use tensor.tensor.Range + + (** Define the value for each coordinate ks *) + let ghost function dleaky_relu (alpha : real) (x : tensor real) : data real = + fun ks -> + if valid ks x.dims then + let v = x.data ks in + if Real.(v >= 0.0) then v else Real.(alpha * v) + else + (* If not valid, map to the transformed background *) + if Real.(x.background >= 0.0) + then x.background + else Real.(alpha * x.background) + + let ghost function opleaky_relu (alpha : real) (x : tensor real) : tensor real = + ensures { result.dims = x.dims } + ensures { result.data = dleaky_relu alpha x } + let bg = if Real.(x.background >= 0.0) + then x.background + else Real.(alpha * x.background) in + { dims = x.dims; + data = dleaky_relu alpha x; + background = bg } +end + +(* Concrete level *) +module CtensorLeakyRelu + use tensor.libtensor.Ctensor + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.OPLeakyRelu + +let ctensor_leaky_relu (alpha : float) (x r : ctensor) = + (*proof*) + requires { valid_tensor x } + requires { valid_tensor r } + requires { tensor x ~ tensor r } + requires { + let xb = (tensor x).background in + (tensor r).background = if Real.(xb >= 0.0) then xb else Real.(to_real alpha * xb) + } + ensures { tensor r = opleaky_relu (to_real alpha) (tensor x) } + + (* Capture logic states here *) + let ghost x_logic = tensor x in + let ghost r_init = tensor r in + + (*qed*) + + let m = cdim_size r.t_dims r.t_rank in + let a_real = to_real alpha in + + for i = 0 to m - 1 do + (*proof*) + invariant { 0 <= i <= m } + invariant { (tensor r).background = r_init.background } + + (* Physical invariant *) + invariant { forall k. 0 <= k < i -> + value_at r.t_data k = + let v = value_at x.t_data k in + if Real.(v >= 0.0) then v else Real.(a_real * v) + } + + (* Logical invariant using the ghost x_logic *) + invariant { forall ks. valid ks x_logic.dims -> + let idx = offset ks x_logic.dims in + if idx < i then + (tensor r).data ks = + let v = x_logic.data ks in + if Real.(v >= 0.0) then v else Real.(a_real * v) + else + (tensor r).data ks = r_init.data ks + } + (*qed*) + + let v = x.t_data[i] in + r.t_data[i] <- if (f32 0.0) .<= v then v else alpha .* v + done + + (*proof*) + ; let ghost expected = opleaky_relu a_real x_logic in + (* Step 1: Prove shape equality *) + assert { (tensor r).dims = expected.dims } + (* Step 2: Prove functional equality of data (already done) *) + ; assert { (tensor r).data = expected.data } + (* Step 3: Prove background equality (already implied by invariant) *) + ; assert { (tensor r).background = expected.background } + (* Step 4: Final record equality *) + ; assert { tensor r == expected } + (*qed*) + +end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu/proof.json b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu/proof.json new file mode 100644 index 00000000..ce1cb333 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu/proof.json @@ -0,0 +1,28 @@ +{ + "profile": [], + "proofs": { + "CTensorLeakyReLU": { + "ctensor_leakyrelu": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.6.2", "time": 0.019 }, + { "prover": "alt-ergo@2.6.2", "time": 0.021 }, + { "prover": "alt-ergo@2.6.2", "time": 0.018 }, + { "prover": "alt-ergo@2.6.2", "time": 0.035 }, + { "prover": "alt-ergo@2.6.2", "time": 0.035 }, + { "prover": "alt-ergo@2.6.2", "time": 0.037 }, + { "prover": "alt-ergo@2.6.2", "time": 0.023 }, + { "prover": "alt-ergo@2.6.2", "time": 0.026 }, + { "prover": "alt-ergo@2.6.2", "time": 0.032 }, + { "prover": "alt-ergo@2.6.2", "time": 0.077 }, + { "prover": "alt-ergo@2.6.2", "time": 0.023 }, + { "prover": "alt-ergo@2.6.2", "time": 0.023 } + ] + } + }, + "OPLeakyReLU": { + "dleakyrelu": { "prover": "alt-ergo@2.6.2", "time": 0.017 }, + "opleakyrelu": { "prover": "alt-ergo@2.6.2", "time": 0.017 } + } + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu_driver.drv new file mode 100644 index 00000000..2c0f54a8 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/leakyrelu/leakyrelu_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module leakyrelu.CtensorLeakyRelu + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/less/Makefile b/safety-related-profile/sonnx/ops/spec/formal/less/Makefile new file mode 100644 index 00000000..d50438e5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/less/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'less' +# Ce Makefile est exécuté depuis : ops/spec/formal/less/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/less/generated_doc +CODE_DIR = ../../../code/less/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'less' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'less'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "less operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_LESS = -D c -D less_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_LESS) less.CTensorLess + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/less/less.mlw b/safety-related-profile/sonnx/ops/spec/formal/less/less.mlw new file mode 100644 index 00000000..7bac2703 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/less/less.mlw @@ -0,0 +1,76 @@ +(** OP-Less Tensor Operation *) +module OPLess + use real.Real + use tensor.tensor.Tensor + use tensor.tensor.Range + + let ghost function less (x y : real) : real = + if Real.(x < y) then 1.0 else 0.0 + + let ghost function dless (x y : tensor real) : data real + = fun ks -> + if valid ks x.dims && valid ks y.dims then + less (x.data ks) (y.data ks) + else + 0.0 + + let ghost function opless (x y : tensor real) : tensor real + requires { x ~= y } + ensures { result.dims = x.dims } + ensures { result.background = 0.0 } + ensures { result.data = dless x y } + (*proof*) + = { dims = x.dims ; data = dless x y ; background = 0.0 } + (*qed*) + +end + +module CTensorLess + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use OPLess + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + +let ctensor_less (x y r : ctensor) = + (*proof*) + requires { valid_tensor x } + requires { valid_tensor y } + requires { valid_tensor r } + requires { tensor x ~= tensor y ~= tensor r } + (*qed*) + ensures { tensor r == opless (tensor x) (tensor y) } + + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + + + + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + (if Real.(value_at x.t_data k < value_at y.t_data k) + then 1.0 else 0.0) + } + (*qed*) + + + r.t_data[i] <- + if Real.(x.t_data[i] .< y.t_data[i]) then + (f32 1.0) + else + (f32 0.0) + done + + + (*proof*) + ; assert { tensor r == opless (tensor x) (tensor y) } + (*qed*) +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/less/less_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/less/less_driver.drv new file mode 100644 index 00000000..f46b758a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/less/less_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module less.CTensorLess + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/log/log.mlw b/safety-related-profile/sonnx/ops/spec/formal/log/log.mlw new file mode 100644 index 00000000..9a4d8d3f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/log/log.mlw @@ -0,0 +1,24 @@ +(** + Specification of Log operation on tensors. + *) +module Log + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + use real.Real + use real.Log + let function log (a : tensor real) : tensor real = + ensures { + forall i. if a.value[i] > 0.0 then result.value[i] = log a.value[i] + else if a.value[i] = 0.0 then result.value[i] = -infinity + else result.value[i] = nan + } + { + shape = a.shape ; + value = fun i -> if a.value[i] > 0.0 then log a.value[i] + else if a.value[i] = 0.0 then -infinity + else nan ; + } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/lstm/lstm.mlw b/safety-related-profile/sonnx/ops/spec/formal/lstm/lstm.mlw new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/Makefile b/safety-related-profile/sonnx/ops/spec/formal/matmul/Makefile new file mode 100644 index 00000000..82e289a3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/matmul/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'matmul' +# Ce Makefile est exécuté depuis : ops/spec/formal/matmul/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/matmul/generated_doc +CODE_DIR = ../../../code/matmul/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'matmul' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'matmul'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "matmul operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_MATMUL = -D c -D matmul_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_MATMUL) matmul.COPMatmul + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul.mlw b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul.mlw new file mode 100644 index 00000000..845b598d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul.mlw @@ -0,0 +1,215 @@ +module OPMatmul + + use tensor.Tensor + use std.Int + use list.List + use tensor.Range + use list.Length + use int.Int + use real.Real + + + let rec ghost function dot_product (a b: tensor real) (row col i iter: int) : real + variant { Int.(iter - i) } + = + if Int.(>=) i iter then + 0.0 + else + let a_val = a.data (Cons row (Cons i Nil)) in + let b_val = b.data (Cons i (Cons col Nil)) in + Real.(a_val * b_val + dot_product a b row col Int.(i + 1) iter) + + + let rec lemma dot_product_aux_step (a b: tensor real) (row col i k iter: int) + requires { Int.(i <= k < iter) } + variant { Int.(k - i) } + ensures { + let a_val = a.data (Cons row (Cons k Nil)) in + let b_val = b.data (Cons k (Cons col Nil)) in + dot_product a b row col i Int.(k + 1) = + Real.(dot_product a b row col i k + a_val * b_val) + } + = + if i < k then Real.(dot_product_aux_step a b row col (i + 1) k iter) else () + + function dmatmul ( a b: tensor real) (a_rows b_cols : int) : data real + = + let iter = get_dim a.dims 1 in + let y_shape = Cons a_rows (Cons b_cols Nil) in + fun ks -> + if valid ks y_shape then + let row = get_dim ks 0 in + let col = get_dim ks 1 in + dot_product a b row col 0 iter + else + a.background + + + let ghost function matmul ( a b: tensor real) : tensor real + requires { a.background = b.background } + requires { length a.dims = length b.dims = 2 } + requires { get_dim a.dims 1 = get_dim b.dims 0 } + ensures { result.background = a.background } + ensures { result.dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) } + ensures { length result.dims = 2 } + ensures { result.data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) } + = + { dims = Cons (get_dim a.dims 0) (Cons (get_dim b.dims 1) Nil) ; data = dmatmul a b (get_dim a.dims 0) (get_dim b.dims 1) ; background = a.background } + +end + + +module COPMatmul + + use OPMatmul + use tensor.Tensor + use tensor.Range + use real.Real + use int.Int + use list.List + use layout.CFlat + + use libtensor.CTensor + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + use std.Int + use list.Length + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + + let matmul (a b r: ctensor) : bool = + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { value_at a.t_dims 1 = value_at b.t_dims 0 } + requires { value_at r.t_dims 0 = value_at a.t_dims 0 } + requires { value_at r.t_dims 1 = value_at b.t_dims 1 } + requires { a.t_rank = b.t_rank = r.t_rank = 2 } + requires { length (tensor a).Tensor.dims = length (tensor b).Tensor.dims = length (tensor r).Tensor.dims = 2 } + requires { get_dim (tensor a).Tensor.dims 1 = get_dim (tensor b).Tensor.dims 0 } + + ensures { result -> tensor r = matmul (tensor a) (tensor b) } + + let ghost matmul_result = matmul (tensor a) (tensor b) in + assert { matmul_result.dims = ivector r.t_dims r.t_rank }; + + let rows = a.t_dims[0] in + let cols = b.t_dims[1] in + let iter = a.t_dims[1] in + let ref flag = False in + let ref sum = (f32 0.0) in + let a_coords_array = malloc (to_uint32 2) in + let b_coords_array = malloc (to_uint32 2) in + let r_coords_array = malloc (to_uint32 2) in + + if is_not_null a_coords_array && is_not_null b_coords_array && is_not_null r_coords_array then begin + flag <- True; + for i = 0 to rows - 1 do + invariant { sum = 0.0 } + invariant { + forall ii:int, jj:int. + 0 <= ii < i -> + 0 <= jj < cols -> + (tensor r).Tensor.data (Cons ii (Cons jj Nil)) = + dot_product (tensor a) (tensor b) ii jj 0 iter + } + for j = 0 to cols - 1 do + invariant { sum = 0.0 } + invariant { + forall ii, jj:int. + 0 <= ii < i -> + 0 <= jj < cols -> + (tensor r).Tensor.data (Cons ii (Cons jj Nil)) = + dot_product (tensor a) (tensor b) ii jj 0 iter + } + invariant { + forall jj:int. + 0 <= jj < j -> + (tensor r).Tensor.data (Cons i (Cons jj Nil)) = + dot_product (tensor a) (tensor b) i jj 0 iter + } + for k = 0 to iter - 1 do + invariant { sum = dot_product (tensor a) (tensor b) i j 0 k } + set_ofs a_coords_array 0 i; + set_ofs a_coords_array 1 k; + set_ofs b_coords_array 0 k; + set_ofs b_coords_array 1 j; + + ghost + begin + valid_bounds_2 a_coords_array a.t_dims 0 2; + + valid_bounds_2 b_coords_array b.t_dims 0 2; + + dot_product_aux_step (tensor a) (tensor b) (to_int i) (to_int j) 0 (to_int k) (to_int iter); + end; + + let a_coords = coffset_1 a_coords_array a.t_dims a.t_rank in + let b_coords = coffset_1 b_coords_array b.t_dims b.t_rank in + let a_val = a.t_data[a_coords] in + let b_val = b.t_data[b_coords] in + + assert {Cons ((i)) (Cons ((k)) Nil) = ivector a_coords_array a.t_rank }; + assert { (tensor a).Tensor.data (Cons ((i)) (Cons ((k)) Nil)) = value_at a.t_data a_coords }; + + assert {Cons ((k)) (Cons ((j)) Nil) = ivector b_coords_array b.t_rank }; + assert { (tensor b).Tensor.data (Cons ((k)) (Cons ((j)) Nil)) = value_at b.t_data b_coords }; + + sum <- sum .+ (a_val .* b_val); + + done; + + set_ofs r_coords_array 0 i; + set_ofs r_coords_array 1 j; + let r_coords = coffset_1 r_coords_array r.t_dims r.t_rank in + + assert { Cons i (Cons j Nil) = ivector r_coords_array r.t_rank }; + assert { r_coords = CFlat.offset (Cons i (Cons j Nil)) (ivector r.t_dims r.t_rank) }; + + r.t_data[r_coords] <- sum; + + assert { (tensor r).Tensor.data (Cons i (Cons j Nil)) = value_at r.t_data r_coords }; + + assert { + forall jj:int. + 0 <= jj < j -> + CFlat.offset (Cons i (Cons jj Nil)) (ivector r.t_dims r.t_rank) <> r_coords + }; + + sum <- (f32 0.0); + + done; + + done; + + assert { ivector a.t_dims a.t_rank = Cons (to_int rows) (Cons (to_int iter) Nil) }; + assert { ivector b.t_dims b.t_rank = Cons (to_int iter) (Cons (to_int cols) Nil) }; + assert { ivector r.t_dims r.t_rank = Cons (to_int rows) (Cons (to_int cols) Nil) }; + assert { rows = get_dim (tensor a).Tensor.dims 0 }; + assert { cols = get_dim (tensor b).Tensor.dims 1 }; + assert { iter = get_dim (tensor a).Tensor.dims 1 }; + + assert { tensor r == matmul (tensor a) (tensor b) }; + flag + end + else + flag + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml new file mode 100644 index 00000000..cd17f8e7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml @@ -0,0 +1,705 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml.bak new file mode 100644 index 00000000..cd17f8e7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3session.xml.bak @@ -0,0 +1,705 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz new file mode 100644 index 00000000..ef459157 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz.bak new file mode 100644 index 00000000..ef459157 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul_driver.drv new file mode 100644 index 00000000..56e9da7c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/matmul/matmul_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module matmul.COPMatmul + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/max/max.md b/safety-related-profile/sonnx/ops/spec/formal/max/max.md new file mode 100644 index 00000000..7cb243a6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/max/max.md @@ -0,0 +1,17 @@ +The purpose of this file is to keep trace of some mathematical concepts usefull for future formal specification + +#### Properties +From the mathematical definition of the maximum we have two properties: + +The maximum corresponding shall be larger or equal than each of + +$\forall n \in [1, N], \forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... ~~~~ y_{i,j,k,l...} \geq z^n_{i,j,k,l...}$ + +$\forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... \exists n \in [1, N] ~~| ~~~~ y_{i,j,k,l...} = z^n_{i,j,k,l...}$ + +The same properties written in function of unboardcasted inputs are: + +$\forall n \in [1, N], \forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... ~~~~ y_{i,j,k,l...} \geq x^n_{f(i,I_n,\max_{m \in [1, N] } I_m),f(j,J_n,\max_{m \in [1, N] } J_m),f(k,K_n,\max_{m \in [1, N] } K_m),f(l,L_n,\max_{m \in [1, N] } L_m)...}$ + +$\forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... \exists n \in [1, N] ~~| ~~~~ y_{i,j,k,l...} = x^n_{f(i,I_n,\max_{m \in [1, N] } I_m),f(j,J_n,\max_{m \in [1, N] } J_m),f(k,K_n,\max_{m \in [1, N] } K_m),f(l,L_n,\max_{m \in [1, N] } L_m)...}$ + diff --git a/safety-related-profile/sonnx/ops/spec/formal/max/max.mlw b/safety-related-profile/sonnx/ops/spec/formal/max/max.mlw new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool.mlw b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool.mlw new file mode 100644 index 00000000..535323f5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool.mlw @@ -0,0 +1,930 @@ +module ExtendedReal + + use real.Real + use real.MinMax + + type extended_real = Finite real | NaN | PosInf | NegInf + + predicate is_nan (e: extended_real) = + match e with + | NaN -> true + | _ -> false + end + + predicate is_pos_inf (e: extended_real) = + match e with + | PosInf -> true + | _ -> false + end + + predicate is_neg_inf (e: extended_real) = + match e with + | NegInf -> true + | _ -> false + end + + predicate is_finite (e: extended_real) = + match e with + | Finite _ -> true + | _ -> false + end + + val (=) (e1 e2: extended_real) : bool + ensures { + result <-> ( + match e1, e2 with + | Finite r1, Finite r2 -> r1 = r2 + | NaN, NaN -> true + | PosInf, PosInf -> true + | NegInf, NegInf -> true + | _, _ -> false + end + ) + } + + + function add_extended_real (e1 e2: extended_real) : extended_real = + match e1, e2 with + | Finite r1, Finite r2 -> Finite (r1 + r2) + | NaN, _ -> NaN + | _, NaN -> NaN + | PosInf, NegInf -> NaN + | NegInf, PosInf -> NaN + | PosInf, _ -> PosInf + | _, PosInf -> PosInf + | NegInf, _ -> NegInf + | _, NegInf -> NegInf + end + + function max_extended_real (e1 e2: extended_real) : extended_real = + match e1, e2 with + | Finite r1, Finite r2 -> Finite (max r1 r2) + | NaN, _ -> NaN + | _, NaN -> NaN + | PosInf, _ -> PosInf + | _, PosInf -> PosInf + | NegInf, e -> e + | e, NegInf -> e + end + + function min_extended_real (e1 e2: extended_real) : extended_real = + match e1, e2 with + | Finite r1, Finite r2 -> Finite (min r1 r2) + | NaN, _ -> NaN + | _, NaN -> NaN + | NegInf, _ -> NegInf + | _, NegInf -> NegInf + | PosInf, e -> e + | e, PosInf -> e + end + +end + + +module ExtendedCFloat + + use real.Real + use ExtendedReal + + type float = + | FFinite real + | FNaN + | FPosInf + | FNegInf + + function to_extended_real (f: float) : extended_real = + match f with + | FFinite r -> Finite r + | FNaN -> NaN + | FPosInf -> PosInf + | FNegInf -> NegInf + end + + type f32 = < float 8 24 > (* single precision literals *) + type f64 = < float 11 53 > (* double precision literals *) + + + meta coercion function to_extended_real + + + val predicate is_nan (f: float) : bool + ensures { result <-> is_nan (to_extended_real f) } + + val predicate is_pos_inf (f: float) : bool + ensures { result <-> is_pos_inf (to_extended_real f) } + + val predicate is_neg_inf (f: float) : bool + ensures { result <-> is_neg_inf (to_extended_real f) } + + val predicate is_finite (f: float) : bool + ensures { result <-> is_finite (to_extended_real f) } + + val predicate ( .= ) (a b : float) : bool + ensures { result <-> to_extended_real a = to_extended_real b } + + val function f32 (k : f32) : float + ensures { result = FFinite (f32'real k) } + + val function f64 (k : f64) : float + ensures { result = FFinite (f64'real k) } + + let constant zero = f64 0.0 + let constant one = f64 1.0 + + val function (.+) (a b : float) : float + ensures { result = add_extended_real (to_extended_real a) (to_extended_real b) } + + val function max (a b : float) : float + ensures { result = max_extended_real (to_extended_real a) (to_extended_real b) } + + val function min (a b : float) : float + ensures { result = min_extended_real (to_extended_real a) (to_extended_real b) } + +end + + +module MyCtensor + + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use ExtendedCFloat + use ExtendedReal + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + + (** C Tensor **) + + type farray = ptr float + + type ctensor = { + t_rank : int32 ; + t_dims : iarray ; + t_data : farray ; + } + + (** Dimension List **) + function tensor_dim (t : ctensor) : list int = ivector t.t_dims t.t_rank + (** Number of entries **) + function tensor_size (t : ctensor) : int = vdim t.t_dims t.t_rank + (** Valid index predicate **) + predicate valid_index (k : list int) (t : ctensor) = valid k (tensor_dim t) + (** Empty tensor predicate **) + predicate empty_tensor (t : ctensor) = t.t_rank = 0 + + (** Valid tensor **) + (**valid dimensions**) + (**valid_range [0, Number of entries] are valid indices**) + (**data writable??**) + predicate valid_tensor (t : ctensor) = + dimension t.t_dims t.t_rank /\ + valid_range t.t_data 0 (tensor_size t) /\ + writable t.t_data + + (*Coordinate Index for ctensor*) + function tensor_offset (k : list int) (t : ctensor) : int = offset k (tensor_dim t) + + function tensor_value_at (k : list int) (t : ctensor) : extended_real = + if valid_index k t then + value_at t.t_data (tensor_offset k t) + else Finite 0.0 + + function tensor_value (t : ctensor) : list int -> extended_real = + fun k -> tensor_value_at k t + + let ghost function tensor (t : ctensor) : tensor extended_real + (*proof*) + requires { valid_tensor t } + ensures { result.dims = tensor_dim t } + ensures { result.data = tensor_value t } + ensures { result.background = Finite 0.0 } + (*qed*) + = { + dims = pure { tensor_dim t } ; + data = pure { tensor_value t } ; + background = Finite 0.0 ; + } + + let ctensor_create (ds : iarray) (n : int32) : ctensor = + (*proof*) + requires { dimension ds n } + ensures { empty_tensor result \/ valid_tensor result } + ensures { empty_tensor result \/ result.t_rank = n } + ensures { empty_tensor result \/ result.t_dims = ds } + (*qed*) + let m = cdim_size ds n in + let vs = malloc (to_uint32 m) in + { + t_rank = if is_null vs then 0 else n ; + t_dims = ds ; + t_data = vs ; + } + + let ctensor_clear (r : ctensor) = + requires { valid_tensor r } + ensures { tensor r = Tensor.zero (Finite 0.0) (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = Finite 0.0 } + (*qed*) + r.t_data[i] <- f32 0.0 + done + (*proof*) + ; assert { tensor r == Tensor.zero (Finite 0.0) (tensor_dim r) } + (*qed*) + + let ctensor_reset (r : ctensor) (v : float) = + requires { valid_tensor r } + ensures { tensor r = Tensor.const (to_extended_real v) (Finite 0.0) (tensor_dim r) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { forall k. 0 <= k < i -> value_at r.t_data k = to_extended_real v } + (*qed*) + r.t_data[i] <- v + done + (*proof*) + ; assert { tensor r == Tensor.const (to_extended_real v) (Finite 0.0) (tensor_dim r) } + (*qed*) +end + + +module OPMaxPool2d + + use tensor.tensor.Tensor + use real.Real + use int.Int + use tensor.std.Int + use list.List + use int.EuclideanDivision + use tensor.tensor.Range + use list.Length + use ExtendedReal + + + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons d ds -> get_dim_positive ds end + + + function maxpoolDims (x:tensor extended_real) (str_h str_w :int) (ker_h ker_w :int) + (dir_h dir_w :int) (pad_top pad_left pad_bottom pad_right :int) : list int + = + let dY_0 = get_dim x.dims 0 in + let dY_1 = get_dim x.dims 1 in + + let alpha = get_dim x.dims 2 + pad_top + pad_bottom in + let theta = dir_h * (ker_h - 1) + 1 in + let dY_2 = div (alpha - theta) str_h + 1 in + + let beta = get_dim x.dims 3 + pad_left + pad_right in + let gamma = dir_w * (ker_w - 1) + 1 in + let dY_3 = div (beta - gamma) str_w + 1 in + + Cons dY_0 (Cons dY_1 (Cons dY_2 (Cons dY_3 Nil + ))) + + function coords_from_X_p (x: tensor extended_real) (x_p_coords : list int) ( pad_top pad_left :int) : extended_real + = + let b = get_dim x_p_coords 0 in + let c = get_dim x_p_coords 1 in + let n = (get_dim x_p_coords 2) - pad_top in + let m = (get_dim x_p_coords 3) - pad_left in + let x_coords = Cons b (Cons c (Cons n (Cons m Nil))) in + if valid x_coords x.dims then + x.data x_coords + else + NegInf + + + let rec ghost function w_cools_calculate (x: tensor extended_real) (h: int) (w:int) (ww:int) (b c m n :int) (dil_h dil_w :int) (pad_top pad_left pad_bottom pad_rigth :int) (str_h str_w :int) : extended_real + variant { w - ww } + = + if Int.(ww >= w) then + NegInf + else + let x_h = m * str_h + h * dil_h in + let x_w = n * str_w + ww * dil_w in + let x_val = coords_from_X_p x (Cons b (Cons c (Cons x_h (Cons x_w Nil)))) pad_top pad_left in + max_extended_real x_val (w_cools_calculate x h w (ww+1) b c m n dil_h dil_w pad_top pad_left pad_bottom pad_rigth str_h str_w) + + + let rec lemma w_cools_split (x: tensor extended_real) (h w ww k b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w: int) + requires { ww <= k < w } + variant { k - ww } + ensures { + let x_h = m * str_h + h * dil_h in + let x_w = n * str_w + k * dil_w in + let x_val = coords_from_X_p x (Cons b (Cons c (Cons x_h (Cons x_w Nil)))) pad_top pad_left in + w_cools_calculate x h Int.(k+1) ww b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w = + max_extended_real x_val + (w_cools_calculate x h k ww b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w) + } + = + if ww < k then + w_cools_split x h w Int.(ww+1) k b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w + else + assert { ww = k }; + () + + + let rec ghost function w_lines_calculate (x: tensor extended_real) (h: int) (hh: int) (w:int) (b c m n :int) (dil_h dil_w :int) (pad_top pad_left pad_bottom pad_rigth :int) (str_h str_w :int) : extended_real + variant { h - hh } + = + if Int.(hh >= h) then + NegInf + else + max_extended_real (w_cools_calculate x hh w 0 b c m n dil_h dil_w pad_top pad_left pad_bottom pad_rigth str_h str_w) + (w_lines_calculate x h Int.(hh+1) w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_rigth str_h str_w) + + + let rec lemma w_lines_split (x: tensor extended_real) (h hh k w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w: int) + requires { hh <= k < h } + variant { k - hh } + ensures { + w_lines_calculate x Int.(k+1) hh w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w = + max_extended_real (w_cools_calculate x k w 0 b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w) + (w_lines_calculate x k hh w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w) + + } + = + if hh < k then + w_lines_split x h Int.(hh+1) k w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w + else + () + + + function dmaxpool (x : tensor extended_real) (y_dims: list int) (h w: int) (dir_h dir_w: int) (pad_top pad_left pad_bottom pad_right: int) (str_h str_w: int) : data extended_real + = + fun ks -> + if valid ks y_dims then + let b = get_dim ks 0 in + let c = get_dim ks 1 in + let m = get_dim ks 2 in + let n = get_dim ks 3 in + w_lines_calculate x h 0 w b c m n dir_h dir_w pad_top pad_left pad_bottom pad_right str_h str_w + else + x.background + + + let ghost function maxpool (x : tensor extended_real) (h w: int) (dir_h dir_w: int) (pad_top pad_left pad_bottom pad_right: int) (str_h str_w: int) : tensor extended_real + (*dilations [C1] [C2]*) + requires { dir_h > 0 /\ dir_w > 0 } + (*pads [C1] - yes*) + (*strides [C1]*) + requires { str_h > 0 /\ str_w > 0 } + (* X[C1]*) + requires { length x.dims = 4 } + (*Y[C1]*) + requires { let y_dims = maxpoolDims x str_h str_w h w dir_h dir_w pad_top pad_left pad_bottom pad_right in + get_dim y_dims 2 > 0 /\ get_dim y_dims 3 > 0 + } + (*Add this to informal?*) + requires { h > 0 /\ w > 0} + + ensures { result.dims = maxpoolDims x str_h str_w h w dir_h dir_w pad_top pad_left pad_bottom pad_right } + ensures {let y_dims = maxpoolDims x str_h str_w h w dir_h dir_w pad_top pad_left pad_bottom pad_right in + result.data = dmaxpool x y_dims h w dir_h dir_w pad_top pad_left pad_bottom pad_right str_h str_w + + } + ensures { result.background = x.background } + = + let y_dims = maxpoolDims x str_h str_w h w dir_h dir_w pad_top pad_left pad_bottom pad_right in + { + dims = y_dims ; + data = dmaxpool x y_dims h w dir_h dir_w pad_top pad_left pad_bottom pad_right str_h str_w ; + background = x.background ; + } + +end + + +module COPMaxpool2d + + use OPMaxPool2d + use tensor.tensor.Tensor + use tensor.tensor.Range + use real.Real + use int.Int + use list.List + use tensor.layout.CFlat + + use tensor.libvector.CIndex + use tensor.std.Clib + use mach.int.Int32 + use tensor.std.Int + use list.Length + use tensor.layout.CFlat + use tensor.tensor.Range + + use MyCtensor + use ExtendedCFloat + use ExtendedReal + + + let coords_from_X_p (x: ctensor) (x_p_coords : iarray) ( pad_top pad_left : int32) : (float, bool) + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { valid_range x_p_coords 0 4 } + requires { writable x_p_coords } + requires { in_bounds (value_at x_p_coords 2 - pad_top) } + requires { in_bounds (value_at x_p_coords 3 - pad_left) } + + ensures { let (value, flag) = result in + flag -> coords_from_X_p (tensor x) (ivector x_p_coords 4) (to_int pad_top) (to_int pad_left) = value + } + = + let ref flag = False in + let b = x_p_coords[0] in + let c = x_p_coords[1] in + let n = x_p_coords[2] - pad_top in + let m = x_p_coords[3] - pad_left in + let x_coords_array = malloc (to_uint32 4) in + if is_not_null x_coords_array then begin + x_coords_array[0] <- b ; + x_coords_array[1] <- c ; + x_coords_array[2] <- n ; + x_coords_array[3] <- m ; + + assert { ivector x_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int n) (Cons (to_int m) Nil))) } ; + + let x_coords = coffset x_coords_array x.t_dims x.t_rank in + if x_coords >= 0 then begin + (x.t_data[x_coords], flag) + end + else begin + (FNegInf, flag) + end + end + else begin + flag <- False; + (FNegInf, flag) + end + + + let w_cools_calculate (x: ctensor) (h w: int32) (b c m n: int32) (dil_h dil_w :int32) (pad_top pad_left pad_bottom pad_rigth :int32) (str_h str_w :int32) : (float, bool) + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { w > 0} + + requires { in_bounds (h*dil_h)} + requires { in_bounds (m*str_h)} + requires { in_bounds(m*str_h+h*dil_h) /\ in_bounds( (m*str_h+h*dil_h) - pad_top) } + + requires { forall ww. 0 <= ww < w -> + in_bounds(ww*dil_w) + } + requires { in_bounds(n*str_w)} + requires { forall ww. 0 <= ww < w -> + in_bounds(n*str_w+ww*dil_w) /\ in_bounds( (n*str_w+ww*dil_w) - pad_left) + } + + ensures { + let (value, flag) = result in + flag -> value = w_cools_calculate (tensor x) (to_int h) (to_int w) 0 (to_int b) (to_int c) (to_int m) (to_int n) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_rigth) (to_int str_h) (to_int str_w) + } + = + let ref flag = False in + let ref max_value = FNegInf in + let x_p_coords_array = malloc (to_uint32 4) in + if is_not_null x_p_coords_array then begin + flag <- True; + let x_h = m * str_h + h * dil_h in + for ww = 0 to w-1 do + invariant { max_value = w_cools_calculate (tensor x) (to_int h) ww 0 (to_int b) (to_int c) (to_int m) (to_int n) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_rigth) (to_int str_h) (to_int str_w) } + let x_w = n * str_w + ww * dil_w in + x_p_coords_array[0] <- b ; + x_p_coords_array[1] <- c ; + x_p_coords_array[2] <- x_h; + x_p_coords_array[3] <- x_w ; + let (x_val, aux_flag) = coords_from_X_p x x_p_coords_array pad_top pad_left in + if aux_flag then begin + + ghost w_cools_split (tensor x) (to_int h) (to_int w) 0 (to_int ww) (to_int b) (to_int c) (to_int m) (to_int n) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_rigth) (to_int str_h) (to_int str_w); + + max_value <- max max_value x_val ; + assert { ivector x_p_coords_array 4 = Cons (to_int b) (Cons (to_int c) (Cons (to_int x_h) (Cons (to_int x_w) Nil))) } ; + end + else + return (FNegInf, False) + done ; + (max_value, flag) + end + else + (FNegInf, flag) + + + let w_lines_calculate (x: ctensor) (h w: int32) (b c m n: int32) (dil_h dil_w :int32) (pad_top pad_left pad_bottom pad_rigth :int32) (str_h str_w :int32) : (float, bool) + requires { valid_tensor x } + requires { x.t_rank = 4 } + requires { h > 0 } + requires { w > 0 } + + requires { forall hh. 0 <= hh < h -> + in_bounds (hh*dil_h) + } + requires { in_bounds (m*str_h)} + requires { forall hh. 0 <= hh < h -> + in_bounds(m*str_h+hh*dil_h) /\ in_bounds( (m*str_h+hh*dil_h) - pad_top) + } + + requires { forall ww. 0 <= ww < w -> + in_bounds(ww*dil_w) + } + requires { in_bounds(n*str_w)} + requires { forall ww. 0 <= ww < w -> + in_bounds(n*str_w+ww*dil_w) /\ in_bounds( (n*str_w+ww*dil_w) - pad_left) + } + + ensures { + let (value, flag) = result in + flag -> value = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) (to_int b) (to_int c) (to_int m) (to_int n) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_rigth) (to_int str_h) (to_int str_w) + } + = + let ref flag = False in + let ref max_value = FNegInf in + for hh = 0 to h-1 do + invariant { max_value = w_lines_calculate (tensor x) hh 0 (to_int w) (to_int b) (to_int c) (to_int m) (to_int n) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_rigth) (to_int str_h) (to_int str_w) } + let (value, aux_flag) = w_cools_calculate x hh w b c m n dil_h dil_w pad_top pad_left pad_bottom pad_rigth str_h str_w in + if aux_flag then begin + max_value <- max max_value value ; + end + else + return (FNegInf, False) + done ; + (max_value, True) + + + let rec lemma valid_bounds_2 (ks ds: iarray) (p q: int) + requires { p <= q } + requires { valid_range ks p q } + requires { valid_range ds p q } + requires { pdim ds p q } + requires { forall i. p <= i < q -> 0 <= value_at ks i < value_at ds i } + ensures { Range.valid (islice ks p q) (islice ds p q) } + variant { q - p } + = if p < q then + begin + assert { islice ks p q = Cons (Int32.to_int (value_at ks p)) (islice ks (p+1) q) }; + assert { islice ds p q = Cons (Int32.to_int (value_at ds p)) (islice ds (p+1) q) }; + valid_bounds_2 ks ds (p+1) q + end + + + let c_maxpool (x r: ctensor) (h w: int32) (dil_h dil_w: int32) (pad_top pad_left pad_bottom pad_right: int32) (str_h str_w: int32) : bool + requires { valid_tensor x } + requires { valid_tensor r } + (* X[C1]*) + requires { x.t_rank = r.t_rank = 4 } + requires { length (tensor x).Tensor.dims = length (tensor r).Tensor.dims = 4} + requires { ivector r.t_dims r.t_rank = maxpoolDims (tensor x) str_h str_w h w dil_h dil_w pad_top pad_left pad_bottom pad_right } + (*dilations [C1] [C2]*) + requires { dil_h > 0 /\ dil_w > 0 } + (*pads [C1] - yes*) + (*strides [C1]*) + requires { str_h > 0 /\ str_w > 0 } + (*Y[C1]*) + requires { let y_dims = maxpoolDims (tensor x) str_h str_w h w dil_h dil_w pad_top pad_left pad_bottom pad_right in + get_dim y_dims 2 > 0 /\ get_dim y_dims 3 > 0 + } + (*Add this to informal??*) + requires { h > 0 /\ w > 0} + + (*in_bounds requires*) + requires { forall hh. 0 <= hh < h -> + in_bounds (hh*dil_h) + } + requires { forall mm. 0 <= mm < value_at r.t_dims 2 -> + in_bounds (mm*str_h) + } + requires { forall mm hh. 0 <= hh < h /\ 0 <= mm < value_at r.t_dims 2-> + in_bounds(mm*str_h+hh*dil_h) /\ in_bounds( (mm*str_h+hh*dil_h) - pad_top) + } + + requires { forall ww. 0 <= ww < w -> + in_bounds(ww*dil_w) + } + requires { forall nn. 0 <= nn < value_at r.t_dims 3 -> + in_bounds (nn*str_w) + } + requires { forall nn ww. 0 <= ww < w /\ 0 <= nn < value_at r.t_dims 3 -> + in_bounds(nn*str_w+ww*dil_w) /\ in_bounds( (nn*str_w+ww*dil_w) - pad_left) + } + ensures { result -> tensor r = maxpool (tensor x) (to_int h) (to_int w)(to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) } + = + let b = r.t_dims[0] in + let c = r.t_dims[1] in + let m = r.t_dims[2] in + let n = r.t_dims[3] in + let r_coords_array = malloc (to_uint32 4) in + if is_not_null r_coords_array then begin + for bb = 0 to b-1 do + invariant { + forall bb_ . 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb_ cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + ) + } + r_coords_array[0] <- bb ; + for cc = 0 to c-1 do + invariant { value_at r_coords_array 0 = bb } + invariant { + forall bb_ . 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb_ cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + ) + } + invariant { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + } + r_coords_array[1] <- cc ; + for mm = 0 to m-1 do + invariant { value_at r_coords_array 0 = bb /\ value_at r_coords_array 1 = cc } + invariant { + forall bb_ . 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb_ cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + ) + } + invariant { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + } + r_coords_array[2] <- mm ; + for nn = 0 to n-1 do + invariant { value_at r_coords_array 0 = bb /\ value_at r_coords_array 1 = cc /\ value_at r_coords_array 2 = mm } + invariant { + forall bb_ . 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb_ cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + ) + } + invariant { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc_ mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + ) + } + invariant { + forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc mm_ nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + ) + } + invariant { + forall nn_. 0 <= nn_ < nn -> + let coords = Cons bb (Cons cc (Cons mm (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + value_at r.t_data off = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc mm nn_ (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + } + r_coords_array[3] <- nn ; + let r_coords = coffset r_coords_array r.t_dims r.t_rank in + let (value, flag) = w_lines_calculate x h w bb cc mm nn dil_h dil_w pad_top pad_left pad_bottom pad_right str_h str_w in + if flag then begin + r.t_data[r_coords] <- value; + assert { ivector r_coords_array r.t_rank = Cons bb (Cons cc (Cons mm (Cons nn Nil))) } ; + assert { value = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc mm nn (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) } ; + assert { let r_coords = CFlat.offset (ivector r_coords_array r.t_rank) (ivector r.t_dims r.t_rank) in + value_at r.t_data r_coords = w_lines_calculate (tensor x) (to_int h) 0 (to_int w) bb cc mm nn (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) + }; + + (* Nao é escrito na mesma pos - 4º Ciclo *) + assert { forall nn_. 0 <= nn_ < nn -> + let coords = Cons (bb) (Cons (cc) (Cons (mm) (Cons nn_ Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + }; + + assert { forall nn_. 0 <= nn_ < nn -> + let coords = Cons (bb) (Cons (cc) (Cons (mm) (Cons nn_ Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + }; + + assert { forall nn_. 0 <= nn_ < nn -> + let coords = Cons (bb) (Cons (cc) (Cons (mm) (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + }; + + assert { + forall nn_. 0 <= nn_ < nn -> + let coords = Cons (bb) (Cons (cc) (Cons (mm) (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + }; + + (* Nao é escrito na mesma pos - 3º Ciclo *) + assert { + forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons (bb) (Cons (cc) (Cons mm_ (Cons nn_ Nil)) ) in + coords <> (ivector r_coords_array r.t_rank) + ) + }; + + assert { forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons (bb) (Cons (cc) (Cons mm_ (Cons nn_ Nil)) ) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + }; + assert { forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons (bb) (Cons (cc) (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + }; + assert { + forall mm_. 0 <= mm_ < mm -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons (bb) (Cons (cc) (Cons mm_ (Cons nn_ Nil)) ) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + }; + + (* Nao é escrito na mesma pos - 2º Ciclo*) + assert { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + }; + assert { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + }; + assert { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + }; + assert { + forall cc_. 0 <= cc_ < cc -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + ) + }; + + (*Nao é escrito na mesma pos - 1º Ciclo*) + assert { + forall bb_. 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + coords <> (ivector r_coords_array r.t_rank) + ) + ) + ) + }; + assert { + forall bb_. 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + Range.valid coords (ivector r.t_dims r.t_rank) + ) + ) + ) + }; + assert { + forall bb_. 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + let off1 = CFlat.offset (ivector r_coords_array r.t_rank) dims in + off <> off1 + ) + ) + ) + }; + assert { + forall bb_. 0 <= bb_ < bb -> + (forall cc_. 0 <= cc_ < c -> + (forall mm_. 0 <= mm_ < m -> + (forall nn_. 0 <= nn_ < n -> + let coords = Cons bb_ (Cons cc_ (Cons mm_ (Cons nn_ Nil))) in + let dims = ivector r.t_dims r.t_rank in + let off = CFlat.offset coords dims in + off <> r_coords + ) + ) + ) + }; + end + else + return false + done; + done; + done; + done; + assert { ivector r.t_dims r.t_rank = Cons (to_int b) (Cons (to_int c) (Cons (to_int m) (Cons (to_int n) Nil))) }; + assert { tensor r == maxpool (tensor x) (to_int h) (to_int w) (to_int dil_h) (to_int dil_w) (to_int pad_top) (to_int pad_left) (to_int pad_bottom) (to_int pad_right) (to_int str_h) (to_int str_w) }; + true + end + else + false + +end + diff --git a/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml new file mode 100644 index 00000000..7a24bb2d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml @@ -0,0 +1,793 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml.bak new file mode 100644 index 00000000..7a24bb2d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3session.xml.bak @@ -0,0 +1,793 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz new file mode 100644 index 00000000..85a40ff5 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz.bak new file mode 100644 index 00000000..85a40ff5 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/maxpool/maxpool/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/mul/Makefile b/safety-related-profile/sonnx/ops/spec/formal/mul/Makefile new file mode 100644 index 00000000..3da126eb --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/mul/Makefile @@ -0,0 +1,57 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'mul' +# Ce Makefile est exécuté depuis : ops/spec/formal/mul/ +# ========================================================== + +.PHONY: prove update clean + +#Directories +DOC_DIR = ../../../code/mul/generated_doc +CODE_DIR = ../../../code/mul/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'mul' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'mul'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Mul operator" -o $(DOC_DIR) + +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_MUL = -D c -D mul_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_MUL) mul.CTensorMul + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -rf $(CODE_DIR) + rm -rf $(DOC_DIR) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/mul/mul.mlw b/safety-related-profile/sonnx/ops/spec/formal/mul/mul.mlw new file mode 100644 index 00000000..8f5614b1 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/mul/mul.mlw @@ -0,0 +1,64 @@ +module OPMul + use tensor.tensor.Tensor + use int.Int + use real.Real + use tensor.std.Cfloat + use tensor.tensor.Range + + let ghost function dmul (a b : tensor real) : data real + = fun ks -> + if valid ks a.dims then + a.data ks * b.data ks + else + a.background + + let ghost function opmul (a b : tensor real) : tensor real + requires { a ~= b } + ensures { result ~= a } + ensures { result.data = dmul a b } + = { dims = a.dims ; + data = dmul a b ; + background = a.background } + +end + +module CTensorMul + use int.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use ref.Ref + use OPMul + use tensor.libtensor.CTensor + +let ctensor_mul (a b r : ctensor) = + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + + ensures { tensor r = opmul (tensor a) (tensor b) } + + let m = cdim_size r.t_dims r.t_rank in + + for i = 0 to m-1 do + invariant { + forall k. + 0 <= k < i -> + value_at r.t_data k = + value_at a.t_data k .* + value_at b.t_data k + } + + r.t_data[i] <- a.t_data[i] .* b.t_data[i] + + done; + + assert { tensor r == opmul (tensor a) (tensor b) } + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/mul/mul_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/mul/mul_driver.drv new file mode 100644 index 00000000..c4912809 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/mul/mul_driver.drv @@ -0,0 +1,31 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module mul.CTensorMul + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/neg/neg.mlw b/safety-related-profile/sonnx/ops/spec/formal/neg/neg.mlw new file mode 100644 index 00000000..da7aecb9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/neg/neg.mlw @@ -0,0 +1,18 @@ +(** + Specification of Neg operation on tensors. + *) + +module Neg + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + + let function neg (a : tensor 'a) : tensor 'a = + { + shape = a.shape ; + value = fun i -> - a.value[i] ; + } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/pad/pad.mlw b/safety-related-profile/sonnx/ops/spec/formal/pad/pad.mlw new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/formal/pow/pow.mlw b/safety-related-profile/sonnx/ops/spec/formal/pow/pow.mlw new file mode 100644 index 00000000..0fc8b353 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/pow/pow.mlw @@ -0,0 +1,24 @@ +(** + Specification of Pow operation on tensors with real numbers. + *) +module Pow + use int.Int + use map.Map + use tensor.Shape + use tensor.Tensor + use real.Real + (** Define the pow function *) + let function pow (base : real) (exponent : real) : real = + ensures { result = base ** exponent } + { + base ** exponent + } + (** Define the pow operation on a tensor **) + let function pow_tensor (base_tensor : tensor real) (exponent_tensor : tensor real) : tensor real = + requires { same_shape base_tensor exponent_tensor } + ensures { forall i. result.value[i] = pow (base_tensor.value[i]) (exponent_tensor.value[i]) } + { + shape = base_tensor.shape ; + value = fun i -> pow (base_tensor.value[i]) (exponent_tensor.value[i]) ; + } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/range/range.mlw b/safety-related-profile/sonnx/ops/spec/formal/range/range.mlw new file mode 100644 index 00000000..3b8c30f3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/range/range.mlw @@ -0,0 +1,54 @@ +module OPRange + use int.Int + use real.Real + use list.List + use tensor.Tensor + use real.Truncate + use int.MinMax + use real.FromInt + use tensor.Range + use list.Length + + + predicate is_scalar_tensor (t : tensor real) = + t.dims = Nil /\ t.data = (fun _ -> t.background) + + let ghost function calculate_num_elements_y (s_background l_background d_background: real) : int + requires { d_background <> 0.0 } + ensures { Int.(>=) result 0 } + ensures { d_background > 0.0 /\ s_background <= l_background -> + s_background + (from_int result) * d_background >= l_background } + ensures { d_background < 0.0 /\ s_background >= l_background -> + s_background + (from_int result) * d_background <= l_background } + = + max (ceil((l_background - s_background) / d_background)) 0 + + + let ghost function calculate_y_data (s_background d_background: real) (y_dims: list int) : data real + requires { d_background <> 0.0 } + requires { Int.(<=) (length y_dims) 1 } + ensures { forall ks. valid ks y_dims /\ y_dims <> Nil -> (exists i:int. Int.(<=) 0 i /\ Int.(<) i (size y_dims) /\ + result ks = s_background + (from_int i) * d_background) } + = + fun ks -> + if valid ks y_dims then + match ks with + | Cons i Nil -> + let idx = from_int i in + s_background + (idx * d_background) + + | _ -> 0.0 + end + else + 0.0 + + let ghost function oprange (s l d: tensor real) : tensor real + requires { is_scalar_tensor(s) /\ is_scalar_tensor(l) /\ is_scalar_tensor(d) } + requires { d.background <> 0.0 } + = + let num_elements = calculate_num_elements_y s.background l.background d.background in + let y_dims = if Int.(>) num_elements 0 then Cons (num_elements) Nil else Nil in + let y_data = calculate_y_data s.background d.background y_dims in + { dims = y_dims; data = y_data; background = 0.0 } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml new file mode 100644 index 00000000..8e5a8028 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml.bak new file mode 100644 index 00000000..8e5a8028 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3session.xml.bak @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz new file mode 100644 index 00000000..869ad6b4 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz.bak new file mode 100644 index 00000000..869ad6b4 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/range/range/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/relu/Makefile b/safety-related-profile/sonnx/ops/spec/formal/relu/Makefile new file mode 100644 index 00000000..d4cd72b7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/relu/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'relu' +# Ce Makefile est exécuté depuis : ops/spec/formal/relu/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/relu/generated_doc +CODE_DIR = ../../../code/relu/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'relu' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'relu'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "relu operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_RELU = -D c -D relu_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_RELU) relu.CTensorReLU + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/relu/relu.mlw b/safety-related-profile/sonnx/ops/spec/formal/relu/relu.mlw new file mode 100644 index 00000000..8795bfc7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/relu/relu.mlw @@ -0,0 +1,74 @@ +(** OP-ReLU Tensor Operation *) +module OPReLU + use real.Real + use tensor.tensor.Tensor + use tensor.tensor.Range + + let ghost function relu (x : real) : real = + if x < Real.(0.0) then + 0.0 + else + x + + let ghost function drelu (x : tensor real) : data real + = fun ks -> + if valid ks x.dims then + relu (x.data ks) + else + x.background + + let ghost function oprelu (x : tensor real) : tensor real + ensures { result.dims = x.dims } + ensures { result.background = x.background } + ensures { result.data = drelu x } + (*proof*) + = { dims = x.dims ; data = drelu x ; background = x.background } + (*qed*) + +end + +module CTensorReLU + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use OPReLU + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + +let ctensor_relu (x r : ctensor) = + (*proof*) + requires { valid_tensor x } + requires { valid_tensor r } + requires { tensor x ~= tensor r } + (*qed*) + ensures { tensor r = oprelu (tensor x) } + + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(value_at x.t_data k < 0.0) then + 0.0 + else + value_at x.t_data k + } + (*qed*) + + r.t_data[i] <- + if Real.(x.t_data[i] .< (f32 0.0)) then + (f32 0.0) + else + x.t_data[i] + done + + (*proof*) + ; assert { tensor r == oprelu (tensor x) } + (*qed*) +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/relu/relu_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/relu/relu_driver.drv new file mode 100644 index 00000000..6407cc14 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/relu/relu_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module relu.CTensorReLU + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/reshape/reshape.mlw b/safety-related-profile/sonnx/ops/spec/formal/reshape/reshape.mlw new file mode 100644 index 00000000..45814ef2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/reshape/reshape.mlw @@ -0,0 +1,188 @@ +module OPReshape + use tensor.Tensor + use tensor.Range + use layout.CFlat + use int.Int + use real.Real + use std.Int + use list.List + use list.Length + use int.EuclideanDivision + + let rec lemma get_dim_positive (ds : list int) + requires { positive ds } + ensures { forall i. 0 <= i < length ds -> 0 < get_dim ds i } + variant { ds } + = match ds with + | Nil -> () + | Cons _ ds -> get_dim_positive ds end + + + function zero_clamp (x_dims s_dims : list int) : list int + = + match x_dims, s_dims with + | Cons x xs, Cons s ss -> if s = 0 then + Cons x (zero_clamp xs ss) + else + Cons s (zero_clamp xs ss) + | _ -> Nil + end + + + function inferred_dim (x_dims s_dims : list int) (x_size s_size : int) : list int + = + match s_dims with + | Cons s ss -> if (s = -1) then + let inferred = div x_size (- s_size) in + Cons inferred ss + else + Cons s (inferred_dim x_dims ss x_size s_size) + | Nil -> Nil + end + + + function calculate_dims (x: tensor 'a) (s: tensor int) (allowzero: int) : list int + = + let s_dims = + if allowzero = 0 then + (zero_clamp x.dims s.dims) + else + s.dims + in + inferred_dim x.dims s_dims (size x.dims) (size s_dims) + + + function dreshape (x: tensor 'a) (y_shape: list int) : data 'a + = + fun ks -> + if valid ks y_shape then + let y_coords_flat = offset ks y_shape in + let x_coords = index y_coords_flat x.dims in + x.data x_coords + else + x.background + + + let ghost function reshape (x: tensor 'a) (s: tensor int) (allowzero: int) : tensor 'a + (* allowzero C[1] *) + requires { allowzero = 0 \/ allowzero = 1 } + (* allowzero C[2] *) + requires { let rank = get_dim s 0 in + allowzero = 0 -> ( + (forall i. 0 <= i < rank -> + (s.data (Cons i Nil) = 0 -> i < length x.dims)) + ) + } + (* allowzero C[3] *) + requires { let rank = get_dim s 0 in + allowzero = 0 -> ( + (forall i. 0 <= i < rank -> s.data (Cons i Nil) >= -1) /\ + ((exists j. 0 <= j < rank -> s.data (Cons j Nil) = -1) -> + (forall d. 0 <= d < length x.dims -> get_dim x.dims d <> 0)) + (* (forall d. 0 <= d < length x.dims -> get_dim x.dims d <> 0)) *) + ) + } + requires { let rank = get_dim s 0 in + allowzero = 1 -> ( + (forall i. 0 <= i < rank -> + (s.data (Cons i Nil) > 0 \/ + (s.data (Cons i Nil) = 0 -> ((exists d. 0 <= d < length x.dims -> get_dim x.dims d = 0) /\ + (forall z. 0 <= z < rank -> s.data (Cons z Nil) <> -1))) \/ + (s.data (Cons i Nil) = -1 -> (forall z. 0 <= z < rank -> s.data (Cons z Nil) <> 0)) + ) + ) + ) + } + (* X C[1] *) + requires { let y_shape = calculate_dims x s allowzero in + size x.dims = size y_shape } + (* S is a 1D tensor *) + requires { length s.dims = 1} + (* S C[2] *) + requires { let rank = get_dim s 0 in + forall i, j: int. (0 <= i < rank /\ 0 <= j < rank) -> + (s.data (Cons i Nil) = -1 /\ s.data (Cons j Nil) = -1) -> (i = j) + } + + ensures { result.dims = calculate_dims x s allowzero } + ensures { result.background = x.background } + ensures { let y_shape = calculate_dims x s allowzero in + result.data = dreshape x y_shape } + = + let y_shape = calculate_dims x s allowzero in + { dims = y_shape ; data = dreshape x y_shape ; background = x.background } + +end + +module COPReshape + use OPReshape + use tensor.Tensor + use list.List + use list.Length + use int.Int + use libvector.CIndex + use std.Clib + use mach.int.Int32 + use std.Cfloat + use specifictensor.CFloatTensor + use specifictensor.CInt32Tensor + use tensor.Range + + let reshape (x r: ctensorfloat) (s: ctensorint32) (allowzero: int32) + (* Valid tensors *) + requires { CFloatTensor.valid_tensor x } + requires { CFloatTensor.valid_tensor r } + requires { CInt32Tensor.valid_tensor s } + (* Shape match *) + requires { let y_shape = calculate_dims (CFloatTensor.tensor x CFloatTensor.zero) (CInt32Tensor.tensor s CInt32Tensor.zero) allowzero in + (ivector r.CFloatTensor.t_dims r.CFloatTensor.t_rank) = y_shape } + (* allowzero C[1] *) + requires { allowzero = 0 \/ allowzero = 1 } + (* allowzero C[2] *) + requires { let rank = value_at s.t_dims 0 in + allowzero = 0 -> ( + (forall i. 0 <= i < rank -> + (value_at s.t_data i = 0 -> i < length (CFloatTensor.tensor x CFloatTensor.zero).dims)) + ) + } + (* allowzero C[3] *) + requires { let rank = value_at s.t_dims 0 in + allowzero = 0 -> ( + (forall i. 0 <= i < rank -> value_at s.t_data i >= -1) /\ + ((exists j. 0 <= j < rank -> value_at s.t_data j = -1) -> + (forall d. 0 <= d < length (CFloatTensor.tensor x CFloatTensor.zero).dims -> value_at x.CFloatTensor.t_dims d <> 0)) + (* (forall d. 0 <= d < length x.dims -> get_dim x.dims d <> 0)) *) + ) + } + requires { let rank = value_at s.t_dims 0 in + allowzero = 1 -> ( + (forall i. 0 <= i < rank -> + (value_at s.t_data i > 0 \/ + (value_at s.t_data i = 0 -> ((exists d. 0 <= d < length (CFloatTensor.tensor x CFloatTensor.zero).dims -> value_at x.CFloatTensor.t_dims d = 0) /\ + (forall z. 0 <= z < rank -> value_at s.t_data z <> -1))) \/ + (value_at s.t_data i = -1 -> (forall z. 0 <= z < rank -> value_at s.t_data z <> 0)) + ) + ) + ) + } + (* X C[1] *) + requires { let y_shape = calculate_dims (CFloatTensor.tensor x CFloatTensor.zero) (CInt32Tensor.tensor s CInt32Tensor.zero) allowzero in + size (CFloatTensor.tensor x CFloatTensor.zero).dims = size y_shape } + (* S is a 1D tensor *) + requires { s.t_rank = 1 } + (* S C[2] *) + requires { let rank = value_at s.t_dims 0 in + forall i, j: int. (0 <= i < rank /\ 0 <= j < rank) -> + (value_at s.t_data i = -1 /\ value_at s.t_data j = -1) -> (i = j) + } + + ensures { (CFloatTensor.tensor r CFloatTensor.zero) = reshape (CFloatTensor.tensor x CFloatTensor.zero) (CInt32Tensor.tensor s CInt32Tensor.zero) allowzero } + = + let ghost value = reshape (CFloatTensor.tensor x CFloatTensor.zero) (CInt32Tensor.tensor s CInt32Tensor.zero) (to_int allowzero) in + let m = cdim_size r.CFloatTensor.t_dims r.CFloatTensor.t_rank in + for i = 0 to m - 1 do + invariant { forall k. 0 <= k < i -> value_at r.CFloatTensor.t_data k = value_at x.CFloatTensor.t_data k } + r.CFloatTensor.t_data[i] <- x.CFloatTensor.t_data[i] + done; + assert { (CFloatTensor.tensor r CFloatTensor.zero) == reshape (CFloatTensor.tensor x CFloatTensor.zero) (CInt32Tensor.tensor s CInt32Tensor.zero) allowzero } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/shape/shape.mlw b/safety-related-profile/sonnx/ops/spec/formal/shape/shape.mlw new file mode 100644 index 00000000..28560735 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/shape/shape.mlw @@ -0,0 +1,83 @@ +module OPShape + use list.List + use int.Int + use tensor.Range + use list.Length + use tensor.Tensor + use list.Mem + + let rec ghost function my_nth (n: int) (l:list int) : int + requires { n >= 0 /\ n < length(l) } + requires { length(l) > 0 } + variant { length(l) } + = + match l with + | Cons h t -> + if n = 0 then + h + else + my_nth (n-1) t + end + + let ghost function clamp_boundaries (x_rank: int) (start: int) (end_: int) : (int, int) + ensures { let (s,e) = result in 0 <= s /\ e <= x_rank } + = + let start = if start < 0 then start + x_rank else start in + let end_ = if end_ < 0 then end_ + x_rank else end_ in + let start = if start < 0 then 0 else start in + let end_ = if end_ > x_rank then x_rank else end_ in + (start, end_) + + let rec ghost function calculate_y_dims (x_dims: list int) (start: int) (end_: int) : list int + requires { positive x_dims } + requires { 0 <= start /\ end_ <= length x_dims } + ensures {start = 0 /\ end_ >= start -> length result = end_ - start } + ensures { forall i. 0 <= i < length result /\ start = 0 -> my_nth i result = my_nth i x_dims } + ensures { positive result } + = + if start >= end_ then + Nil + else + match x_dims with + | Nil -> Nil + | Cons h t -> + if start > 0 then + calculate_y_dims t (start - 1) (end_ - 1) + else + Cons h (calculate_y_dims t 0 (end_ - 1)) + end + + (*Try to find a smarter way to describe 2nd requires*) + let ghost function dshape_ (x: tensor int) (start: int) (end_: int) (result_y: list int ) (y_dims: list int): data int + requires { 0 <= start /\ end_ <= length x.dims } + requires { result_y = calculate_y_dims x.dims start end_ } + requires { y_dims = if length result_y > 0 then Cons (length result_y) Nil else Nil } + ensures { forall ks. valid ks y_dims -> length ks <= 1 /\ + match ks with + | Cons k0 Nil -> result ks = my_nth k0 result_y + | _ -> true + end + } + ensures { forall ks. not (valid ks y_dims) -> result ks = x.background } + = + fun ks -> + if valid ks y_dims then + match ks with + | Cons k0 Nil -> my_nth k0 result_y + | Nil -> x.background + | _ -> absurd + end + else + x.background + + + let ghost function opshape (x: tensor int) (start: int) (end_: int) : tensor int + = + let x_rank = length x.dims in + let (clamped_start, clamped_end) = clamp_boundaries x_rank start end_ in + let result_y = calculate_y_dims x.dims clamped_start clamped_end in + let y_dims = if length result_y > 0 then Cons (length result_y) Nil else Nil in + let y_data = dshape_ x clamped_start clamped_end result_y y_dims in + { dims = y_dims; data = y_data; background = x.background } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml new file mode 100644 index 00000000..04e9bae8 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml.bak new file mode 100644 index 00000000..04e9bae8 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3session.xml.bak @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz new file mode 100644 index 00000000..dff7ea61 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz.bak new file mode 100644 index 00000000..dff7ea61 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/shape/shape/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/sigmoid/sigmoid.mlw b/safety-related-profile/sonnx/ops/spec/formal/sigmoid/sigmoid.mlw new file mode 100644 index 00000000..a2b0e2e7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/sigmoid/sigmoid.mlw @@ -0,0 +1,87 @@ +(** OP-Sigmoid Tensor Operation *) +module OPSigmoid + use real.Real + use real.ExpLog + use tensor.tensor.Tensor + use tensor.tensor.Range + + + let ghost function sigmoid (x : real ) : real = + if x < Real.(0.0) then + 0.0 * exp x / (1.0 + exp x) (** j'ai ajoute * 0.0 pour aider au debug pour pas que l'exponentielle complique **) + else + 1.0 + 0.0 * 1.0 / (1.0 + exp (-x)) (** j'ai ajoute * 0.0 + 1.0 pour aider au debug pour pas que l'exponentielle complique **) + + let ghost function dsigmoid (x : tensor real) : data real + = fun ks -> + if valid ks x.dims then + sigmoid (x.data ks) + else + x.background + + + let ghost function opsigmoid (x : tensor real) : tensor real + ensures { result.dims = x.dims } + ensures { result.background = x.background } + ensures { result.data = dsigmoid x } + (*proof*) + = { dims = x.dims ; data = dsigmoid x ; background = x.background } + (*qed*) + + + +end + + + +module CTensorSigmoid + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use OPSigmoid + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + +let ctensor_sigmoid (x r : ctensor) = + (*proof*) + requires { valid_tensor x } + requires { valid_tensor r } + requires { tensor x ~= tensor r } + (*qed*) + ensures { tensor r = opsigmoid (tensor x) } + + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m - 1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(value_at x.t_data k < 0.0) then + 0.0 (**exp (value_at x.t_data k) / (1.0 + exp (value_at x.t_data k))**) (** j'ai mis * 0.0 pour aider au debug pour pas que l'exponentielle complique **) + else + 1.0 (**1.0 / (1.0 + exp (- value_at x.t_data k))**) (** j'ai mis * 1.0 pour aider au debug pour pas que l'exponentielle complique **) + } + (*qed*) + + + r.t_data[i] <- + if Real.(x.t_data[i] .< (f32 0.0)) then + (f32 0.0) (*exp(x.t_data[i]) /. (f32 1.0 +. exp(x.t_data[i]))*) (** j'ai mis * 0.0 pour aider au debug pour pas que l'exponentielle complique **) + else + (f32 1.0) (*(f32 1.0) /. (f32 1.0 +. exp(-x.t_data[i]))*) (** j'ai mis * 1.0 pour aider au debug pour pas que l'exponentielle complique **) + done + + + (*proof*) + ; assert { tensor r == opsigmoid (tensor x) } + (*qed*) + +end + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/slice/slice.mlw b/safety-related-profile/sonnx/ops/spec/formal/slice/slice.mlw new file mode 100644 index 00000000..f3e7f729 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/slice/slice.mlw @@ -0,0 +1,557 @@ +module OPSlice + use tensor.Tensor + use tensor.Range + use int.Int + use int.EuclideanDivision + use list.List + use list.Length + use list.Nth + use option.Option + use list.Map + use list.Mem + + + function get (o: option int): int = + match o with + | Some v -> v + | None -> -1 + end + + function my_nth (l: list int) (i: int): int = + match nth i l with + | Some d -> d + | None -> -1 + end + + + function head (l: list int): int = + match l with + | Cons d _ -> d + | Nil -> -1 + end + + + let ghost function normalize_axis (dims: list int) (axis_dim : list int) (axis: tensor int): tensor int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { length dims = size axis.dims } + requires { axis.dims = axis_dim } + (*A [C2]*) + requires { forall ks. valid ks axis_dim -> -length dims <= axis.data ks <= length dims - 1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim -> + (mod ((axis.data ks1) + length dims) (length dims)) = (mod ((axis.data ks2) + length dims) (length dims)) -> ks1 = ks2 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (*Normalization*) + ensures { forall ks. valid ks axis_dim -> 0 <= result.data ks < length dims } + ensures {forall ks. valid ks axis_dim -> axis.data ks < 0 -> result.data ks = axis.data ks + length dims } + ensures {forall ks. valid ks axis_dim -> axis.data ks >= 0 -> result.data ks = axis.data ks } + (*Index Uniqueness*) + ensures { forall ks1 ks2. valid ks1 axis_dim /\ valid ks2 axis_dim -> result.data ks1 = result.data ks2 -> ks1 = ks2 } + (*Same dimension, Same background*) + ensures { result ~= axis} + (*proof*) + = let data = fun ks -> + if valid ks axis_dim then + let a = axis.data ks in + if a < 0 then a + length dims else a + else axis.background + in { dims = axis_dim ; data = data ; background = axis.background } + + let ghost function normalize_start (dims: list int) (start_dims: list int) (start axis : tensor int): tensor int + (*Additionally*) + requires { start_dims = start.dims } + (*1D Index Tensors*) + requires { length start.dims = length axis.dims = 1 } + (*X [C1]*) + requires { length dims = size start.dims = size axis.dims } + (*Redundant with 2 previous requires*) + requires { start ~ axis } + (*A [C2]*) + requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims -> + (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 } + (*S [C2]*) + requires { forall ks. valid ks start.dims -> -my_nth dims (axis.data ks) <= start.data ks <= my_nth dims (axis.data ks) - 1 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (*Normalization*) + ensures { forall ks. valid ks start.dims -> 0 <= result.data ks < my_nth dims (axis.data ks) } + ensures { forall ks. valid ks start.dims -> start.data ks < 0 -> result.data ks = start.data ks + my_nth dims (axis.data ks) } + ensures { forall ks. valid ks start.dims -> start.data ks >= 0 -> result.data ks = start.data ks } + (*Same dimension, Same background*) + ensures { result ~= start } + (*proof*) + = let data = fun ks -> + if valid ks start_dims then + let s = start.data ks in + let axis_index = axis.data ks in + if s < 0 then s + my_nth dims axis_index else s + else start.background + in { dims = start_dims ; data = data ; background = start.background } + + let ghost function normalize_end (dims: list int) (ends_dims: list int) (end_ axis steps: tensor int): tensor int + (*Additionally*) + requires { ends_dims = end_.dims } + (*1D Index Tensors*) + requires { length end_.dims = length axis.dims = length steps.dims = 1 } + (**X [C1]*) + requires { length dims = size end_.dims = size axis.dims = size steps.dims } + (*Redundant with previous requires*) + requires { end_ ~ axis ~ steps } + (*A [C2]*) + requires { forall ks. valid ks axis.dims -> 0 <= axis.data ks <= length dims -1} + (*A [C3]*) + requires { forall ks1 ks2. valid ks1 axis.dims /\ valid ks2 axis.dims -> + (mod (axis.data ks1 + length dims) (length dims)) = (mod (axis.data ks2 + length dims) (length dims)) -> ks1 = ks2 } + (*K [C2]*) + requires { forall ks. valid ks steps.dims -> steps.data ks <> 0 } + (*E [C2]*) + requires { forall ks. valid ks end_.dims -> steps.data ks > 0 -> + let idx = my_nth dims (axis.data ks) in + -(idx) <= end_.data ks <= idx } + (*E [C3]*) + requires { forall ks. valid ks end_.dims -> steps.data ks < 0 -> + let idx = my_nth dims (axis.data ks) in + -(idx) - 1 <= end_.data ks <= idx - 1 } + (*Rank > 0, see this better*) + requires { length dims > 0 } + (**Normalization*) + ensures { forall ks. valid ks end_.dims -> + let idx = my_nth dims (axis.data ks) in + let step = steps.data ks in + let e = end_.data ks in + let res = if e < 0 then e + idx else e in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + ensures { forall ks. valid ks end_.dims -> + let idx = my_nth dims (axis.data ks) in + let e = end_.data ks in + result.data ks = (if e < 0 then e + idx else e) + } + (*Same dimension, Same background*) + ensures { result ~= end_ } + (*proof*) + = let data = fun ks -> + if valid ks ends_dims then + let e = end_.data ks in + let axis_index = axis.data ks in + if e < 0 then e + my_nth dims axis_index else e + else end_.background + in { dims = ends_dims ; data = data ; background = end_.background } + + + let rec ghost function dY_shape_rec (x:tensor 'a) (s e k a: tensor int) (t: list int) (i: int) : list int + (*1D Index Tensors*) + requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension - CHECK THIS*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*Normalize End*) + requires { forall ks. valid ks e.dims -> + let idx = my_nth x.dims (a.data ks) in + let step = k.data ks in + let e_value = e.data ks in + let res = if e_value < 0 then e_value + idx else e_value in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + (*K [C2]*) + (*requires { forall ks. valid ks k.dims -> k.data ks <> 0 }*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + (*Real Index t*) + requires { forall h. 0 <= h < length x.dims -> mem h t } + (*R6*) + requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks } + (*R7*) + requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks } + (*Y [C2]*) + requires { + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 + } + requires { 0 <= i < length x.dims } + variant { length x.dims - i } + ensures { length result = length x.dims - i } + ensures { forall z. 0 <= z < length result -> my_nth result z > 0 } + = if i < (length x.dims - 1) then + let real_index = my_nth t i in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in + let y = div space (k.data (Cons real_index Nil)) + f in + Cons y (dY_shape_rec x s e k a t (i+1)) + else + let real_index = my_nth t i in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let f = if (mod space (k.data (Cons real_index Nil))) = 0 then 0 else 1 in + let y = div space (k.data (Cons real_index Nil)) + f in + Cons y Nil + + + let ghost function dY_shape (x : tensor 'a) (s e k a: tensor int) (t: list int) : list int + (*1D Index Tensors*) + requires { length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims = length t } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*Normalize End*) + requires { forall ks. valid ks e.dims -> + let idx = my_nth x.dims (a.data ks) in + let step = k.data ks in + let e_value = e.data ks in + let res = if e_value < 0 then e_value + idx else e_value in + (step > 0 -> 0 <= res <= idx) /\ + (step < 0 -> -1 <= res <= idx - 1) + } + (*K [C2]*) + requires { forall ks. valid ks k.dims -> k.data ks <> 0 } + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + (*Real Index t*) + requires { forall h. 0 <= h < length x.dims -> mem h t } + (*R6*) + requires { forall ks. valid ks s.dims -> k.data ks > 0 -> s.data ks <= e.data ks } + (*R7*) + requires { forall ks. valid ks s.dims -> k.data ks < 0 -> s.data ks >= e.data ks } + (*Y [C2]*) + requires { + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e.data (Cons real_index Nil) - s.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 + } + ensures { length result = length x.dims } + ensures { forall z. 0 <= z < length result -> my_nth result z > 0 } + = dY_shape_rec x s e k a t 0 + + + (*Given an axis, find the index of that specific axis representation in axis tensor*) + (*axis -> tensor normalized*) + (*x_rank -> rank of input X*) + (*i -> looking value*) + (*j -> current searching index*) + let rec ghost function find_j (axis: tensor int) (x_rank: int) (i: int) (j: int) : int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { x_rank = size axis.dims } + (*Rank > 0, see this better*) + requires { x_rank > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 } + requires { 0 <= i < x_rank } + requires { 0 <= j < x_rank } + requires { forall h. 0 <= h < j -> axis.data (Cons h Nil) <> i } + requires { exists z. 0 <= z < x_rank /\ axis.data (Cons z Nil) = i } + ensures { 0 <= result < x_rank } + ensures { axis.data (Cons result Nil) = i } + variant { x_rank - j } + = if j < (x_rank - 1) then + if axis.data (Cons j Nil) = i then + j + else find_j axis x_rank i (j+1) + else + if axis.data (Cons j Nil) = i then + j + else + absurd + + (*REVIEW THIS AXIOM*) + axiom axis_data_surjective: + forall axis: tensor int, x_rank: int, v: int. + length axis.dims = 1 /\ + x_rank = size axis.dims /\ + x_rank > 0 /\ + (forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank - 1) /\ + (forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) x_rank) = (mod (axis.data (Cons z2 Nil) + x_rank) x_rank) -> z1 = z2) /\ + 0 <= v < x_rank + -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v + + (*Builds a list with the real indices representation for each index in axis*) + (* axis = [2,3,1,0]*) + (* real_axis = [3,2,0,1]*) + (* axis -> tensor normalized*) + (* axis_rank -> rank of axis tensor = rank of input tensor*) + (* i -> current index in the axis tensor*) + let rec ghost function real_index_aux (axis: tensor int) (x_rank: int) (i: int) : list int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*X [C1]*) + requires { x_rank = size axis.dims } + (*Rank > 0, see this better*) + requires { x_rank > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < x_rank -> 0 <= axis.data (Cons z Nil) <= x_rank -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < x_rank /\ 0 <= z2 < x_rank -> + (mod (axis.data (Cons z1 Nil) + x_rank) (x_rank)) = (mod (axis.data (Cons z2 Nil) + x_rank) (x_rank)) -> z1 = z2 } + requires { 0 <= i < x_rank } + requires { forall v. i <= v < x_rank -> exists h. 0 <= h < x_rank /\ axis.data (Cons h Nil) = v } + ensures { forall k. 0 <= k < length result -> + let z = i + k in + axis.data (Cons (my_nth result k) Nil) = z } + ensures { length result = x_rank - i } + variant { x_rank - i } + = if i < (x_rank - 1) then + let j = find_j axis x_rank i 0 in + Cons j (real_index_aux axis x_rank (i+1)) + else + let j = find_j axis x_rank i 0 in + Cons j Nil + + let ghost function real_index (axis: tensor int): list int + (*1D Index Tensors*) + requires { length axis.dims = 1 } + (*Rank > 0, see this better*) + requires { size axis.dims > 0 } + (*A [C2]*) + requires { forall z. 0 <= z < size axis.dims -> 0 <= axis.data (Cons z Nil) <= size axis.dims -1} + (*A [C3]*) + requires { forall z1 z2. 0 <= z1 < size axis.dims /\ 0 <= z2 < size axis.dims -> + (mod (axis.data (Cons z1 Nil) + size axis.dims) (size axis.dims)) = (mod (axis.data (Cons z2 Nil) + size axis.dims) (size axis.dims)) -> z1 = z2 } + requires { forall v. 0 <= v < size axis.dims -> exists h. 0 <= h < size axis.dims /\ axis.data (Cons h Nil) = v } + ensures { forall k. 0 <= k < length result -> + let z = k in + axis.data (Cons (my_nth result k) Nil) = z } + ensures { length result = size axis.dims } + = let x_rank = size axis.dims in + real_index_aux axis x_rank 0 + + + let rec ghost function dX_coords_rec (x: tensor 'a) (coords t: list int) (s k a: tensor int) (i: int) : list int + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { length coords = length x.dims } + requires { 0 <= i < length coords } + ensures { length result = length x.dims - i } + + variant { length coords - i } + = if i < ((length coords)-1) then + let real_index = my_nth t i in + let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in + Cons original_index (dX_coords_rec x coords t s k a (i+1)) + else + let real_index = my_nth t i in + let original_index = s.data (Cons real_index Nil) + my_nth coords i * k.data (Cons real_index Nil) in + Cons original_index Nil + + + let ghost function dX_coords (x: tensor 'a) (coords t: list int) (s k a: tensor int): list int = + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { length coords = length x.dims } + ensures { length result = length x.dims } + dX_coords_rec x coords t s k a 0 + + let ghost function dslice (x : tensor 'a) (s k a : tensor int) (dy_shape t: list int) : data 'a + (*1D Index Tensors*) + requires { length s.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ k ~ a } + (*Normalize Axis*) + requires { forall ks. valid ks a.dims -> 0 <= a.data ks < length x.dims } + (*Index Uniqueness*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> a.data ks1 = a.data ks2 -> ks1 = ks2 } + (*Normalize Start*) + requires { forall ks. valid ks s.dims -> 0 <= s.data ks < my_nth x.dims (a.data ks) } + (*K [C2]*) + requires { forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + requires { forall ks. valid ks dy_shape -> length ks = length x.dims } + (*proof*) + = fun ks -> + if valid ks dy_shape then + let x_coords = dX_coords x ks t s k a in + x.data x_coords + else x.background + + let ghost function opslice (x: tensor 'a) (s e k a: tensor int): tensor 'a + (*Additional dslice*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + positive y_shape /\ forall ks. valid ks y_shape -> length ks = length x.dims } + + (*Additional dY_shape*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + forall z. 0 <= z < length t -> k.data (Cons (my_nth t z) Nil) <> 0 } + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + forall h. 0 <= h < length x.dims -> mem h t } + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let t = real_index a_normalized in + let s_normalized = normalize_start (x.dims) s.dims s a_normalized in + let e_normalized = normalize_end (x.dims) e.dims e a_normalized k in + forall z. 0 <= z < length x.dims -> + let real_index = my_nth t z in + let space = e_normalized.data (Cons real_index Nil) - s_normalized.data (Cons real_index Nil) in + let step = k.data (Cons real_index Nil) in + div space step + (if mod space step = 0 then 0 else 1) > 0 } + + (*Additional normalize Axis*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks a_normalized.dims -> 0 <= a_normalized.data ks <= length x.dims -1} + (*Necessary because of real_index but redundant, maybe check real_index and change this*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall z. 0 <= z < size a_normalized.dims -> 0 <= a_normalized.data (Cons z Nil) <= size a_normalized.dims -1} + + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks1 ks2. valid ks1 a_normalized.dims /\ valid ks2 a_normalized.dims -> + (mod (a_normalized.data ks1 + length x.dims) (length x.dims)) = (mod (a_normalized.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 } + (*Necessary because of real_index but redundant, maybe check real_index and change this*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall z1 z2. 0 <= z1 < size a_normalized.dims /\ 0 <= z2 < size a_normalized.dims -> + (mod (a_normalized.data (Cons z1 Nil) + size a_normalized.dims) (size a_normalized.dims)) = (mod (a_normalized.data (Cons z2 Nil) + size a_normalized.dims) (size a_normalized.dims)) -> z1 = z2 } + + (*Additional normalize Start*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length s.dims = length a_normalized.dims = 1 } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length x.dims = size s.dims = size a_normalized.dims } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + s ~ a_normalized } + (*Additional normalize End*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length e.dims = length a_normalized.dims = length k.dims = 1 } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + length x.dims = size e.dims = size a_normalized.dims = size k.dims } + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + e ~ a_normalized ~ k } + (*1D Index Tensors*) + requires {length s.dims = length e.dims = length k.dims = length a.dims = 1 } + (*X Tensor as at least 1 dimension*) + requires { length x.dims > 0} + (*X [C1]*) + requires {length x.dims = size s.dims = size e.dims = size k.dims = size a.dims } + (*Redudant with 2 previous requires*) + requires { s ~ e ~ k ~ a } + (*A [C2]*) + (*requires { forall i. 0 <= i < length x.dims -> -length x.dims <= a.data (Cons i Nil) <= length x.dims -1}*) + requires { forall ks. valid ks a.dims -> -length x.dims <= a.data ks <= length x.dims -1} + (*A [C3]*) + (*requires {forall i j. 0 <= i < length x.dims /\ 0 <= j < length x.dims -> ( mod (a.data (Cons i Nil) + length x.dims) (length x.dims) ) = (mod (a.data (Cons j Nil) + length x.dims) (length x.dims)) -> i = j }*) + requires { forall ks1 ks2. valid ks1 a.dims /\ valid ks2 a.dims -> + ( mod (a.data ks1 + length x.dims) (length x.dims) ) = (mod (a.data ks2 + length x.dims) (length x.dims)) -> ks1 = ks2 } + (*K [C2]*) + (*requires { forall i. 0 <= i < length x.dims -> k.data(Cons i Nil) <> 0 }*) + requires { forall ks. valid ks k.dims -> k.data ks <> 0 } + (*S [C2]*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks s.dims -> + let axis_index = a_normalized.data ks in + - (my_nth x.dims axis_index) <= s.data ks <= (my_nth x.dims axis_index) - 1 } + (*E [C2]*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks e.dims -> + let axis_index = a_normalized.data ks in + let step = k.data ks in + let e_val = e.data ks in + (step > 0 -> - (my_nth x.dims axis_index) <= e_val <= (my_nth x.dims axis_index))} + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + forall ks. valid ks e.dims -> + let axis_index = a_normalized.data ks in + let step = k.data ks in + let e_val = e.data ks in + (step < 0 -> - (my_nth x.dims axis_index) - 1 <= e_val <= (my_nth x.dims axis_index) - 1) } + (*S [C3], E [C3], K [C3]*) + (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) > 0 -> s.data (Cons i Nil) <= e.data (Cons i Nil) }*) + (*requires { forall i. 0 <= i < length x.dims -> k.data (Cons i Nil) < 0 -> s.data (Cons i Nil) >= e.data (Cons i Nil) }*) + (*R6*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + forall ks. valid ks s_normalized.dims -> k.data ks > 0 -> s_normalized.data ks <= e_normalized.data ks } + (*R7*) + requires { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + forall ks. valid ks s_normalized.dims -> k.data ks < 0 -> s_normalized.data ks >= e_normalized.data ks } + (*X [C2]*) + ensures { length result.dims = length x.dims /\ result.background = x.background } + (*Y [C2]*) + ensures { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + result.dims = y_shape } + ensures { let a_normalized = normalize_axis (x.dims) a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + result.data = dslice x s_normalized k a_normalized y_shape t } + (*proof*) + = let a_normalized = normalize_axis x.dims a.dims a in + let s_normalized = normalize_start x.dims s.dims s a_normalized in + let e_normalized = normalize_end x.dims e.dims e a_normalized k in + let t = real_index a_normalized in + let y_shape = dY_shape x s_normalized e_normalized k a_normalized t in + + { dims = y_shape; data = dslice x s_normalized k a_normalized y_shape t; background = x.background} +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml new file mode 100644 index 00000000..7fabc11c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml.bak new file mode 100644 index 00000000..7fabc11c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3session.xml.bak @@ -0,0 +1,455 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz new file mode 100644 index 00000000..2a8dbf92 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz.bak new file mode 100644 index 00000000..2a8dbf92 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/slice/slice/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/softmax/Makefile b/safety-related-profile/sonnx/ops/spec/formal/softmax/Makefile new file mode 100644 index 00000000..a19823b2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/softmax/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'softmax' +# Ce Makefile est exécuté depuis : ops/spec/formal/softmax/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/softmax/generated_doc +CODE_DIR = ../../../code/softmax/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'softmax' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'softmax'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "softmax operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_SOFTMAX = -D c -D softmax_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_SOFTMAX) softmax.CtensorSoftmax + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax.mlw b/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax.mlw new file mode 100644 index 00000000..cf21ba16 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax.mlw @@ -0,0 +1,584 @@ +(* + ========================================================== + Formal specification of Softmax + Based on ONNX Softmax version 13 + ========================================================== + + This file specifies the abstract behavior of Softmax. + + It contains: + - Softmax over real tensors. + - Softmax over floating-point-like tensors with NaN, +inf and -inf. + + This is a specification-level file. + It is not yet the concrete C-extractible implementation. +*) + +module SoftmaxReal + + use int.Int + + (* + Integer comparison wrappers. + + We define them before importing real.Real to avoid ambiguity between + integer comparisons and real comparisons. + *) + predicate ile (a:int) (b:int) = a <= b + predicate ilt (a:int) (b:int) = a < b + + use real.Real + use list.List + use list.Length + use tensor.tensor.Tensor + use tensor.tensor.Range + + (* + Abstract exponential. + + Only the positivity of exp is required for Softmax. + *) + function exp (x: real) : real + + axiom exp_positive: + forall x: real. exp x > 0.0 + + (* + axis_dim dims axis + + Abstract accessor for the size of the tensor shape along axis. + + We use this instead of get_dim because get_dim is not visible in your + current Why3 environment. + *) + function axis_dim (dims: list int) (axis: int) : int + + axiom axis_dim_positive: + forall dims: list int, axis: int. + ile 0 axis -> + ilt axis (length dims) -> + positive dims -> + ilt 0 (axis_dim dims axis) + + (* + replace_axis ks axis j + + Given: + ks = [i0, ..., iaxis, ..., ir-1] + + returns: + [i0, ..., j, ..., ir-1] + + This expresses that Softmax only varies the coordinate along the + selected axis while keeping all other coordinates fixed. + *) + let rec ghost function replace_axis + (ks: list int) (axis: int) (j: int) : list int + variant { ks } + = + match ks with + | Nil -> + Nil + | Cons k rest -> + if axis = 0 then + Cons j rest + else + Cons k (replace_axis rest (axis - 1) j) + end + + (* + axis_max_real x ks axis + + Abstract maximum along the Softmax axis: + + M = max_j X[i0, ..., j, ..., ir-1] + + This is the M used in the stable formulation: + + exp(X[i] - M) / sum_j exp(X[j] - M) + *) + function axis_max_real + (x: tensor real) (ks: list int) (axis: int) : real + + (* + axis_sum_exp_real x ks axis m + + Abstract denominator: + + sum_j exp(X[i0, ..., j, ..., ir-1] - M) + + The summation is only over the selected axis. + *) + function axis_sum_exp_real + (x: tensor real) (ks: list int) (axis: int) (m: real) : real + + axiom axis_sum_exp_real_positive: + forall x: tensor real, ks: list int, axis: int, m: real. + ile 0 axis -> + ilt axis (length x.dims) -> + positive x.dims -> + axis_sum_exp_real x ks axis m > 0.0 + + axiom axis_max_real_upper_bound: + forall x: tensor real, ks: list int, axis j: int. + ile 0 axis -> + ilt axis (length x.dims) -> + ile 0 j -> + ilt j (axis_dim x.dims axis) -> + x.data (replace_axis ks axis j) <= axis_max_real x ks axis + + (* + Stable real Softmax value. + + Y[ks] = + exp(X[ks] - M) + / + sum_j exp(X[replace_axis(ks, axis, j)] - M) + + with: + M = max_j X[replace_axis(ks, axis, j)] + *) + function softmax_value_real + (x: tensor real) (axis: int) (ks: list int) : real + = + let m = axis_max_real x ks axis in + exp ((x.data ks) - m) / axis_sum_exp_real x ks axis m + + function dsoftmax_real (axis: int) (x: tensor real) : data real = + fun ks -> softmax_value_real x axis ks + + (* + Abstract Softmax operator over real tensors. + + Constraints: + - axis >= 0 + - axis < rank(X) + - X has positive dimensions + - Y has same shape as X + - Y follows the stable Softmax definition + *) + val softmax_real (x: tensor real) (axis: int) : tensor real + requires { ile 0 axis } + requires { ilt axis (length x.dims) } + requires { positive x.dims } + ensures { result.dims = x.dims } + ensures { + forall ks: list int. + valid ks x.dims -> + result.data ks = softmax_value_real x axis ks + } + +end + + +module SoftmaxFloat + + use int.Int + + predicate ile (a:int) (b:int) = a <= b + predicate ilt (a:int) (b:int) = a < b + + use real.Real + use list.List + use list.Length + use tensor.tensor.Tensor + use tensor.tensor.Range + + function exp (x: real) : real + + axiom exp_positive: + forall x: real. exp x > 0.0 + + function axis_dim (dims: list int) (axis: int) : int + + axiom axis_dim_positive: + forall dims: list int, axis: int. + ile 0 axis -> + ilt axis (length dims) -> + positive dims -> + ilt 0 (axis_dim dims axis) + + (* + Abstract floating-point value. + + This is a specification-level representation of float16, float and double. + *) + type fvalue = + | FFinite real + | FNaN + | FPosInf + | FNegInf + + predicate is_nan (x: fvalue) = + match x with + | FNaN -> true + | _ -> false + end + + predicate is_pos_inf (x: fvalue) = + match x with + | FPosInf -> true + | _ -> false + end + + predicate is_neg_inf (x: fvalue) = + match x with + | FNegInf -> true + | _ -> false + end + + predicate is_finite (x: fvalue) = + match x with + | FFinite _ -> true + | _ -> false + end + + predicate finite_value (x: fvalue) (r: real) = + match x with + | FFinite v -> r = v + | _ -> false + end + + let rec ghost function replace_axis + (ks: list int) (axis: int) (j: int) : list int + variant { ks } + = + match ks with + | Nil -> + Nil + | Cons k rest -> + if axis = 0 then + Cons j rest + else + Cons k (replace_axis rest (axis - 1) j) + end + + (* + True if the slice along axis contains NaN or +inf. + + Informal rule: + if a slice contains NaN or +inf, all outputs in that slice are NaN. + *) + predicate slice_has_nan_or_posinf + (x: tensor fvalue) (ks: list int) (axis: int) + + axiom slice_has_nan_or_posinf_def: + forall x: tensor fvalue, ks: list int, axis: int. + slice_has_nan_or_posinf x ks axis <-> + exists j: int. + ile 0 j /\ + ilt j (axis_dim x.dims axis) /\ + ( + is_nan (x.data (replace_axis ks axis j)) \/ + is_pos_inf (x.data (replace_axis ks axis j)) + ) + + (* + Maximum over the finite values of the slice. + + It is meaningful only when the slice has no NaN and no +inf. + *) + function axis_max_float + (x: tensor fvalue) (ks: list int) (axis: int) : real + + axiom axis_max_float_upper_bound: + forall x: tensor fvalue, ks: list int, axis j: int, r: real. + ile 0 axis -> + ilt axis (length x.dims) -> + ile 0 j -> + ilt j (axis_dim x.dims axis) -> + finite_value (x.data (replace_axis ks axis j)) r -> + r <= axis_max_float x ks axis + + (* + Exponential contribution for floating values. + + - finite r contributes exp(r - M) + - -inf contributes 0 + - NaN and +inf are handled before this branch and map to NaN output + *) + function exp_term_float (v: fvalue) (m: real) : real = + match v with + | FFinite r -> exp (r - m) + | FNegInf -> 0.0 + | FNaN -> 0.0 + | FPosInf -> 0.0 + end + + function axis_sum_exp_float + (x: tensor fvalue) (ks: list int) (axis: int) (m: real) : real + + axiom axis_sum_exp_float_positive: + forall x: tensor fvalue, ks: list int, axis: int, m: real. + ile 0 axis -> + ilt axis (length x.dims) -> + positive x.dims -> + not (slice_has_nan_or_posinf x ks axis) -> + axis_sum_exp_float x ks axis m > 0.0 + + (* + Floating Softmax value. + + Cases: + 1. If the slice contains NaN or +inf: + output is NaN. + + 2. Else if X[ks] = -inf: + output is finite 0. + + 3. Else if X[ks] is finite r: + output is: + exp(r - M) / sum_j exp_term(X[j], M) + *) + function softmax_value_float + (x: tensor fvalue) (axis: int) (ks: list int) : fvalue + = + if slice_has_nan_or_posinf x ks axis then + FNaN + else + match x.data ks with + | FNegInf -> + FFinite 0.0 + | FFinite r -> + let m = axis_max_float x ks axis in + FFinite (exp (r - m) / axis_sum_exp_float x ks axis m) + | FNaN -> + FNaN + | FPosInf -> + FNaN + end + + function dsoftmax_float (axis: int) (x: tensor fvalue) : data fvalue = + fun ks -> softmax_value_float x axis ks + + val softmax_float (x: tensor fvalue) (axis: int) : tensor fvalue + requires { ile 0 axis } + requires { ilt axis (length x.dims) } + requires { positive x.dims } + ensures { result.dims = x.dims } + ensures { + forall ks: list int. + valid ks x.dims -> + result.data ks = softmax_value_float x axis ks + } + +end + + +(* + ========================================================== + Concrete C-extractible Softmax implementation + ========================================================== + + This module is the concrete part used for C extraction. + + It implements Softmax over a C tensor representation: + + x : input tensor + r : output tensor + axis : axis along which Softmax is computed + + The implementation assumes that: + - x and r have the same rank; + - x and r have the same dimensions; + - axis is valid; + - tensors are stored in flat contiguous memory. + + The algorithm is the usual numerically stable Softmax: + + M = max_j X[j] + Y[i] = exp(X[i] - M) / sum_j exp(X[j] - M) + + The slice traversal uses the standard decomposition: + + outer = product(dims[0 .. axis-1]) + axis_size = dims[axis] + inner = product(dims[axis+1 .. rank-1]) + + For each pair (outer_index, inner_index), we traverse the axis dimension. +*) + +module CtensorSoftmax + + use int.Int + use mach.int.Int32 + use tensor.std.Clib + use tensor.std.Cfloat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + + (* + C exponential. + + This abstract Why3 function is mapped to exp() in C by softmax_driver.drv. + *) + val function c_exp (x: float) : float + + (* + same_dims x r + + Checks that x and r have the same rank and the same dimensions. + + This is implemented as executable code so that c_softmax can return + false instead of assuming blindly that the output tensor is compatible. + *) + let same_dims (x r: ctensor) : bool + requires { valid_tensor x } + requires { valid_tensor r } + = + if x.t_rank <> r.t_rank then + False + else begin + let ref ok = True in + for i = 0 to x.t_rank - 1 do + if x.t_dims[i] <> r.t_dims[i] then + ok <- False + done; + ok + end + + (* + product_dims_range dims first last + + Computes product(dims[first] ... dims[last-1]). + + If first = last, the product is 1. + + This is used to compute: + - inner = product of dimensions after axis + - outer = product of dimensions before axis + *) + let product_dims_range (dims: iarray) (first last: int32) : int32 + requires { valid_range dims 0 last } + requires { 0 <= first } + requires { first <= last } + = + let ref p = 1 in + for i = first to last - 1 do + p <- p * dims[i] + done; + p + + (* + c_softmax x r axis + + Concrete Softmax kernel. + + Parameters: + x : input ctensor + r : output ctensor + axis : Softmax axis + + Return: + True if computation was performed; + False if tensors are incompatible or axis is invalid. + + This function is the one we want to extract into C. + *) + let c_softmax (x r: ctensor) (axis: int32) : bool + requires { valid_tensor x } + requires { valid_tensor r } + = + if x.t_rank <= 0 then + False + else if axis < 0 then + False + else if axis >= x.t_rank then + False + else if not (same_dims x r) then + False + else begin + + (* + rank = number of dimensions. + axis_size = size of the normalized dimension. + *) + let rank = x.t_rank in + let axis_size = x.t_dims[axis] in + + if axis_size <= 0 then + False + else begin + + (* + inner = product of dimensions after axis. + + Example: + shape = [N, C, H, W] + axis = 1 + inner = H * W + + This is the stride between two consecutive values along axis + for a fixed outer/inner position. + *) + let inner = product_dims_range x.t_dims (axis + 1) rank in + + (* + outer = product of dimensions before axis. + + Example: + shape = [N, C, H, W] + axis = 1 + outer = N + *) + let outer = product_dims_range x.t_dims 0 axis in + + (* + For each independent Softmax slice: + - one outer position + - one inner position + *) + for o = 0 to outer - 1 do + for inn = 0 to inner - 1 do + + (* + Base offset of the current slice. + + For every a in [0, axis_size): + offset = base + a * inner + *) + let base = o * axis_size * inner + inn in + + (* + Step 1: compute M = max over the axis slice. + *) + let ref m = x.t_data[base] in + + for a = 1 to axis_size - 1 do + let off = base + a * inner in + let v = x.t_data[off] in + + if m .< v then + m <- v + done; + + (* + Step 2: compute sum_j exp(x_j - M). + *) + let ref sum = zero in + + for a = 0 to axis_size - 1 do + let off = base + a * inner in + let shifted = x.t_data[off] .- m in + let e = c_exp shifted in + sum <- sum .+ e + done; + + (* + Step 3: compute y_i = exp(x_i - M) / sum. + *) + for a = 0 to axis_size - 1 do + let off = base + a * inner in + let shifted = x.t_data[off] .- m in + let e = c_exp shifted in + r.t_data[off] <- e ./ sum + done + + done + done; + + True + end + end + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax_driver.drv new file mode 100644 index 00000000..0faa9151 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/softmax/softmax_driver.drv @@ -0,0 +1,37 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + interface export "#include " + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 == %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module softmax.CtensorSoftmax + interface "#include " + interface "#include " + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + + syntax val c_exp "exp(%1)" +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/softmax/why3find.json b/safety-related-profile/sonnx/ops/spec/formal/softmax/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/softmax/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/sqrt/sqrt.mlw b/safety-related-profile/sonnx/ops/spec/formal/sqrt/sqrt.mlw new file mode 100644 index 00000000..424cb2b3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/sqrt/sqrt.mlw @@ -0,0 +1,22 @@ +(** + Specification of Sqrt operation on tensors. + *) +module Sqrt + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + use real.Real + use real.Sqrt + let function sqrt (a : tensor real) : tensor real = + ensures { + forall i. if a.value[i] >= 0.0 then result.value[i] = sqrt a.value[i] + else result.value[i] = nan + } + { + shape = a.shape ; + value = fun i -> if a.value[i] >= 0.0 then sqrt a.value[i] + else nan ; + } +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/sub/sub.mlw b/safety-related-profile/sonnx/ops/spec/formal/sub/sub.mlw new file mode 100644 index 00000000..c3fe0bb9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/sub/sub.mlw @@ -0,0 +1,18 @@ +(** + Specification of Sub operation on tensors. + *) + +module Sub + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + + let function sub (a b : tensor 'a) : tensor 'a = + { + shape = same a.shape b.shape ; + value = fun i -> a.value[i] - b.value[i] ; + } + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/tanh/Makefile b/safety-related-profile/sonnx/ops/spec/formal/tanh/Makefile new file mode 100644 index 00000000..2810c142 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/tanh/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'tanh' +# Ce Makefile est exécuté depuis : ops/spec/formal/tanh/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/tanh/generated_doc +CODE_DIR = ../../../code/tanh/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'tanh' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'tanh'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "tanh operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_TANH = -D c -D tanh_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_TANH) tanh.CTensorTanh + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh.mlw b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh.mlw new file mode 100644 index 00000000..72b86ab0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh.mlw @@ -0,0 +1,81 @@ +module OPTanh + use tensor.tensor.Tensor + use tensor.tensor.Range + use int.Int + use real.Real + use tensor.std.Cfloat + + val function exp_r (x : real) : real + + val function tanh_r (x : real) : real + ensures { + result = + (exp_r x - exp_r (-x)) / (exp_r x + exp_r (-x)) + } + ensures { + result = + (exp_r (2.0 * x) - 1.0) / (exp_r (2.0 * x) + 1.0) + } + ensures { + result = + (1.0 - exp_r (-2.0 * x)) / (1.0 + exp_r (-2.0 * x)) + } + + let ghost function dtanh (a : tensor real) : data real + = fun ks -> + if valid ks a.dims then + tanh_r (a.data ks) + else + a.background + + let ghost function optanh (a : tensor real) : tensor real + ensures { result ~= a } + ensures { result.data = dtanh a } + = { dims = a.dims ; + data = dtanh a ; + background = a.background } + +end + + +module CTensorTanh + use int.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use ref.Ref + use OPTanh + use tensor.libtensor.CTensor + + val function ctanh (x : float) : float + ensures { result = tanh_r x } + + let ctensor_tanh (a r : ctensor) = + requires { valid_tensor a } + requires { valid_tensor r } + requires { tensor a ~= tensor r } + + ensures { tensor r = optanh (tensor a) } + + let m = cdim_size r.t_dims r.t_rank in + + for i = 0 to m-1 do + invariant { + forall k. + 0 <= k < i -> + value_at r.t_data k = + tanh_r (value_at a.t_data k) + } + + r.t_data[i] <- ctanh a.t_data[i] + + done; + + assert { tensor r == optanh (tensor a) } + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/tanh/tanhByHamza/tanh.mlw b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanhByHamza/tanh.mlw new file mode 100644 index 00000000..16b59c0c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanhByHamza/tanh.mlw @@ -0,0 +1,81 @@ +module OPTanh + use tensor.tensor.Tensor + use tensor.tensor.Range + use int.Int + use real.Real + use tensor.std.Cfloat + + val function exp_r (x : real) : real + + val function tanh_r (x : real) : real + ensures { + result = + (exp_r x - exp_r (-x)) / (exp_r x + exp_r (-x)) + } + ensures { + result = + (exp_r (2.0 * x) - 1.0) / (exp_r (2.0 * x) + 1.0) + } + ensures { + result = + (1.0 - exp_r (-2.0 * x)) / (1.0 + exp_r (-2.0 * x)) + } + + let ghost function dtanh (a : tensor real) : data real + = fun ks -> + if valid ks a.dims then + tanh_r (a.data ks) + else + a.background + + let ghost function optanh (a : tensor real) : tensor real + ensures { result ~= a } + ensures { result.data = dtanh a } + = { dims = a.dims ; + data = dtanh a ; + background = a.background } + +end + + +module CTensorTanh + use int.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use tensor.layout.CFlat + use tensor.libvector.CIndex + use ref.Ref + use OPTanh + use tensor.libtensor.CTensor + + val function ctanh (x : float) : float + ensures { result = tanh_r x } + + let ctensor_tanh (a r : ctensor) = + requires { valid_tensor a } + requires { valid_tensor r } + requires { tensor a ~= tensor r } + + ensures { tensor r = optanh (tensor a) } + + let m = cdim_size r.t_dims r.t_rank in + + for i = 0 to m-1 do + invariant { + forall k. + 0 <= k < i -> + value_at r.t_data k = + tanh_r (value_at a.t_data k) + } + + r.t_data[i] <- ctanh a.t_data[i] + + done; + + assert { tensor r == optanh (tensor a) } + +end diff --git a/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh_driver.drv new file mode 100644 index 00000000..2f684aa0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/tanh/tanh_driver.drv @@ -0,0 +1,33 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + + +module tanh.CTensorTanh + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze.mlw b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze.mlw new file mode 100644 index 00000000..55166658 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze.mlw @@ -0,0 +1,303 @@ +module OPUnsqueeze + use list.List + use int.Int + use list.Mem + use list.Length + use int.EuclideanDivision + use list.Append + use tensor.Range + use tensor.Tensor + use list.NumOcc + use list.Permut + use list.SortedInt + + function my_tail(l: list int) : list int + = + match l with + | Nil -> Nil + | Cons h t -> t + end + + let rec ghost function my_nth (n: int) (l:list int) : int + requires { n >= 0 /\ n < length(l) } + requires { length(l) > 0 } + variant { length(l) } + = + match l with + | Cons h t -> + if n = 0 then + h + else + my_nth (n-1) t + end + + + lemma nth_tail: forall h: int, t: list int, i: int. + 0 <= i < length t -> + my_nth i t = my_nth (i+1) (Cons h t) + + + let rec function partition (x:int) (l:list int) : (l1:list int, l2:list int) + ensures { forall y:int. mem y l1 -> y <= x } + ensures { forall z:int. mem z l2 -> x < z } + ensures { permut (l1 ++ l2) l } + = + match l with + | Nil -> (Nil, Nil) + | Cons y ys -> let (ll, lr) = partition x ys in + if y <= x then + (Cons y ll, lr) + else + (ll, Cons y lr) + end + + + + let rec function quicksort (l:list int): list int + ensures { sorted result } + ensures { permut result l } + variant { length l } + = + match l with + | Nil -> Nil + | Cons x t -> let (l1,l2) = partition x t in + let l1' = quicksort l1 in + let l2' = quicksort l2 in + assert { forall y:int. mem y l1' -> y <= x }; + assert { forall z:int. mem z l2' -> x <= z }; + assert { forall u,v:int. mem u l1' /\ mem v (Cons x l2') -> u <= v }; + l1' ++ (Cons x l2') + end + + + let rec ghost function normalize_axis (axis: list int) (rank: int) : list int + requires { rank >= 0 } + (* Axis Value Domain *) + requires { forall i: int. 0 <= i < length axis -> -rank <= my_nth i axis < rank } + (* Axis Uniqueness *) + requires { forall a1 a2. mem a1 axis /\ mem a2 axis -> + (mod (a1 + rank) rank) = (mod (a2 + rank) rank) -> a1 = a2 } + (* All values on axis exists on normalize axis*) + ensures { length result = length axis } + (* All values on result are normalized *) + ensures { forall i:int. 0 <= i < length result -> + let a = my_nth i axis in + if a < 0 then + my_nth i result = a + rank + else + my_nth i result = a } + ensures { forall i:int. 0 <= i < length result -> 0 <= my_nth i result < rank } + = + match axis with + | Nil -> Nil + | Cons h t -> + if h < 0 then + Cons (h + rank) (normalize_axis t rank) + else + Cons h (normalize_axis t rank) + end + + + let rec ghost function insert_at (x_dims: list int) (a: int) : list int + requires { positive x_dims } + requires { 0 <= a <= length x_dims } + ensures { length result = length x_dims + 1 } + ensures { my_nth a result = 1 } + ensures { length x_dims = 0 -> my_nth 0 result = 1 } + ensures { forall j:int. 0 <= j < a -> ( my_nth j result = my_nth j x_dims) } + ensures { forall j:int. a < j < length x_dims -> ( my_nth (j + 1) result = my_nth j x_dims) } + ensures { positive result } + = + match x_dims with + | Nil -> Cons 1 Nil + | Cons h t -> + if a = 0 then + Cons 1 (Cons h t) + else + Cons h (insert_at t (a - 1)) + end + + + let rec ghost function calculate_y_dims (x_dims: list int) (axis: list int) : list int + requires { positive x_dims} + requires { forall i:int. 0 <= i < length axis -> 0 <= my_nth i axis <= length x_dims + i } + ensures { length result = length x_dims + length axis } + ensures { positive result } + = + match x_dims, axis with + | Nil, Nil -> Nil + | x_dims, Nil -> x_dims + | x_dims, Cons y ys -> calculate_y_dims (insert_at x_dims y) ys + end + + + let rec ghost function decrease_axis (axis: list int): list int + requires { positive axis } + ensures { length result = length axis } + ensures { forall i:int. 0 <= i < length result -> my_nth i result = my_nth i axis - 1 } + ensures { forall i:int. 0 <= i < length result -> my_nth i result >= 0 } + = + match axis with + | Nil -> Nil + | Cons h t -> Cons (h - 1) (decrease_axis t) + end + + let rec lemma sorted_decrease (axis: list int) + requires { sorted axis } + requires { positive axis } + ensures { sorted (decrease_axis axis) } + = + match axis with + | Nil -> Nil + | Cons h t -> + Cons (h - 1) (sorted_decrease t) + end + + let rec ghost function remove_at (y_dims: list int) (a: int) : list int + requires { 0 <= a < length y_dims } + ensures { length result = length y_dims - 1 } + ensures { forall j:int. 0 <= j < a -> my_nth j result = my_nth j y_dims } + ensures { forall j:int. a <= j < length result -> my_nth j result = my_nth (j + 1) y_dims } + variant { a } + = + match y_dims with + | Cons h t -> + if a = 0 then + t + else + Cons h (remove_at t (a - 1)) + end + + lemma my_nth_head: + forall h:int, t:list int. my_nth 0 (Cons h t) = h + + let rec lemma positive_tail (axis: list int) + requires { sorted axis } + requires { forall i, j:int. 0 <= i < length axis /\ 0 <= j < length axis -> + i <> j -> my_nth i axis <> my_nth j axis } + requires { forall i:int. 0 <= i < length axis -> my_nth i axis >= 0 } + ensures { positive (my_tail axis) } + = + match axis with + | Nil -> Nil + | Cons h t -> positive_tail t + end + + let rec ghost function calculate_x_coords (y_coords: list int) (axis: list int) : list int + requires { sorted axis } + requires { forall i:int. 0 <= i < length axis -> 0 <= my_nth i axis < length y_coords - length axis + i} + requires { forall i, j:int. 0 <= i < length axis /\ 0 <= j < length axis -> + i <> j -> my_nth i axis <> my_nth j axis } + variant { length axis } + ensures { length result = length y_coords - length axis } + = + match y_coords, axis with + | Nil, Nil -> Nil + | y_coords, Nil -> y_coords + | y_coords, Cons h t -> calculate_x_coords (remove_at y_coords h) (decrease_axis t) + end + + let rec ghost function build_list (axis: tensor int) (ks: int) : list int + (* 1D Axis *) + requires { length axis.dims = 1 } + (* Valid values for ks, is 0 <= ks < size (axis.dims) *) + requires { 0 <= ks < size (axis.dims) } + variant { size (axis.dims) - ks } + (* size axis data is the same of the size of result *) + ensures { length result = size (axis.dims) - ks } + ensures { forall j:int. 0 <= j < length result -> my_nth j result = axis.data (Cons (ks + j) Nil) } + = + if 0 <= ks < size (axis.dims) - 1 then + Cons (axis.data (Cons ks Nil)) (build_list axis (ks + 1)) + else + Cons (axis.data (Cons ks Nil)) Nil + + + let ghost function dunsqueeze (x: tensor int) (axis: list int) (y_dims: list int): data int + requires { sorted axis } + requires { forall i, j:int. 0 <= i < length axis /\ 0 <= j < length axis -> i <> j -> my_nth i axis <> my_nth j axis } + requires { forall ks:list int. valid ks y_dims -> length ks = length y_dims } + requires { forall i:int. 0 <= i < length axis -> 0 <= my_nth i axis < length y_dims - length axis + i } + = + let x_dims = x.dims in + fun ks -> + if valid ks y_dims then + let x_coords = calculate_x_coords ks axis in + x.data x_coords + else + x.background + + + let rec lemma valid_length (ks: list int) (l_coords: list int) + requires { valid ks l_coords } + ensures { length ks = length l_coords } + variant { ks } + = + match ks, l_coords with + | Nil, Nil -> () + | Cons _ tks, Cons _ tcoords -> valid_length tks tcoords + end + + let ghost function unsqueeze_list (x: tensor int) (axis: list int) : tensor int + (* Axis Value Domain *) + requires { let rank = length x.dims + length axis in + forall i:int. 0 <= i < length axis -> -rank <= my_nth i axis < rank + } + (* Axis Uniqueness *) + requires { let rank = length x.dims + length axis in + forall a1 a2. mem a1 axis /\ mem a2 axis -> + (mod (a1 + rank) rank) = (mod (a2 + rank) rank) -> a1 = a2 + } + requires { let rank = length x.dims + length axis in + let axis_normalized = normalize_axis axis rank in + let axis_sorted_normalized = quicksort axis_normalized in + (forall i:int. 0 <= i < length axis_sorted_normalized -> 0 <= my_nth i axis_sorted_normalized < length x.dims + i) /\ + (forall i, j:int. 0 <= i < length axis_sorted_normalized /\ 0 <= j < length axis_sorted_normalized -> + i <> j -> my_nth i axis_sorted_normalized <> my_nth j axis_sorted_normalized) + } + = + let x_dims = x.dims in + let axis_normalized = normalize_axis axis (length x_dims + length axis) in + let axis_sorted_normalized = quicksort axis_normalized in + let y_dims = calculate_y_dims x_dims axis_sorted_normalized in + let y_data = dunsqueeze x axis_sorted_normalized y_dims in + { dims = y_dims; data = y_data; background = x.background } + + + let ghost function unsqueeze (x: tensor int) (axis: tensor int) : tensor int + (* 1D Axis *) + requires { length axis.dims = 1 } + (* Axis Value Domain *) + requires { let rank = length x.dims + size (axis.dims) in + forall i:int. 0 <= i < size (axis.dims) -> -rank <= axis.data (Cons i Nil) < rank + } + (* Axis Uniqueness *) + requires { let rank = length x.dims + size (axis.dims) in + forall i, j:int. 0 <= i < size (axis.dims) /\ 0 <= j < size (axis.dims) -> + i <> j -> (mod (axis.data (Cons i Nil) + rank) rank) <> (mod (axis.data (Cons j Nil) + rank) rank) + } + (* Axis Value Domain *) + requires { let axis_list = build_list axis 0 in + let rank = length x.dims + length axis_list in + forall i:int. 0 <= i < length axis_list -> -rank <= my_nth i axis_list < rank + } + (* Axis Uniqueness *) + requires { let axis_list = build_list axis 0 in + let rank = length x.dims + length axis_list in + forall a1 a2. mem a1 axis_list /\ mem a2 axis_list -> + (mod (a1 + rank) rank) = (mod (a2 + rank) rank) -> a1 = a2 + } + requires { let axis_list = build_list axis 0 in + let rank = length x.dims + length axis_list in + let axis_normalized = normalize_axis axis_list rank in + let axis_sorted_normalized = quicksort axis_normalized in + (forall i:int. 0 <= i < length axis_sorted_normalized -> 0 <= my_nth i axis_sorted_normalized < length x.dims + i) /\ + (forall i, j:int. 0 <= i < length axis_sorted_normalized /\ 0 <= j < length axis_sorted_normalized -> + i <> j -> my_nth i axis_sorted_normalized <> my_nth j axis_sorted_normalized) + } + = + + let axis_list = build_list axis 0 in + unsqueeze_list x axis_list + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml new file mode 100644 index 00000000..a36fb66b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml.bak new file mode 100644 index 00000000..a36fb66b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3session.xml.bak @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz new file mode 100644 index 00000000..ac4c0d7e Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz.bak new file mode 100644 index 00000000..ac4c0d7e Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/unsqueeze/unsqueeze/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/Makefile b/safety-related-profile/sonnx/ops/spec/formal/where/Makefile new file mode 100644 index 00000000..7bed6a64 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/Makefile @@ -0,0 +1,56 @@ +# ========================================================== +# Makefile pour la vérification formelle de l'opération 'where' +# Ce Makefile est exécuté depuis : ops/spec/formal/where/ +# ========================================================== + +.PHONY: prove update clean +#Directories +DOC_DIR = ../../../code/where/generated_doc +CODE_DIR = ../../../code/where/generated_code/c_code +CDRIVER_DIR = ../common/drivers +LIBTENSOR_DRIVER_DIR = ../common/drivers +SRC_TENSOR = ../common/libs/tensor + + +# ========================================================== +# CIBLES +# ========================================================== + +# --- Vérification des preuves --- +prove: + @echo "------------------------------------------" + @echo "--- Proofs for 'where' operation" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x + +# --- Mise à jour des sessions Why3 --- +update: + @echo "------------------------------------------" + @echo "--- Update Proofs for 'where'" + @echo "------------------------------------------" + @why3find prove *.mlw -l -s -x -m + +doc: + @echo "------------------------------------------" + @echo "--- Documentation" + @echo "------------------------------------------" + @rm -fr html + @why3find doc *.mlw -t "Where operator" -o $(DOC_DIR) +EXTRACT_LIB = -D c -D $(CDRIVER_DIR)/cdriver.drv -L $(SRC_TENSOR) --modular --interface -o $(CODE_DIR) +EXTRACT_WHERE = -D c -D where_driver.drv -D $(LIBTENSOR_DRIVER_DIR)/libtensor.drv -L . --modular --interface -o $(CODE_DIR) + +lib: + @echo "------------------------------------------" + @echo "--- Extraction" + @echo "------------------------------------------" + @rm -fr $(CODE_DIR) + @why3 extract $(EXTRACT_LIB) tensor.libtensor.CTensor + @why3 extract $(EXTRACT_LIB) tensor.libvector.CIndex + @why3 extract $(EXTRACT_WHERE) where.CTensorWhere + @cd $(CODE_DIR) && gcc -c *.c + @find $(CODE_DIR) -type f | sort + +# --- Nettoyage des fichiers générés --- +clean: + rm -f $(CODE_DIR) + rm -f $(DOC_DIR) diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/where.mlw b/safety-related-profile/sonnx/ops/spec/formal/where/where.mlw new file mode 100644 index 00000000..9a89b26e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/where.mlw @@ -0,0 +1,63 @@ +(** OP-Where Tensor Operation *) +module OPWhere + use tensor.tensor.Tensor + + let ghost function dwhere (c : data bool) (a b : data 'a) : data 'a + = fun ks -> if c ks then a ks else b ks + + let ghost function opwhere (c : tensor bool) (a b : tensor 'a) : tensor 'a + requires { a ~= b } + requires { c ~ a ~ b } + ensures { result ~= a ~= b } + ensures { result = dwhere c a b } + (*proof*) + = { dims = c.dims ; data = dwhere c.data a.data b.data ; background = a.background } + (*qed*) + +end + + +module CTensorWhere + use tensor.std.Int + use tensor.std.List + use tensor.std.Clib + use tensor.std.Cfloat + use mach.int.Int32 + use tensor.tensor.Range + use tensor.tensor.Tensor + use OPWhere + use tensor.layout.CFlat + use tensor.libvector.CIndex + use tensor.libtensor.CTensor + + + +let ctensor_where (cond a b r : ctensor) = + (*proof*) + requires { valid_tensor cond } + requires { valid_tensor a } + requires { valid_tensor b } + requires { valid_tensor r } + requires { tensor a ~= tensor b ~= tensor r } + (*qed*) + requires { tensorb cond ~ tensor a ~ tensor b } + ensures { tensor r = opwhere (tensorb cond) (tensor a) (tensor b) } + let m = cdim_size r.t_dims r.t_rank in + for i = 0 to m-1 do + (*proof*) + invariant { + forall k. 0 <= k < i -> + value_at r.t_data k = + if Real.(0.0 < value_at cond.t_data k) + then a.t_data[k] else b.t_data[k] + } + (*qed*) + r.t_data[i] <- + if (f32 0.0) .< cond.t_data[i] then a.t_data[i] else b.t_data[i] + done + (*proof*) + ; assert { tensor r == opwhere (tensorb cond) (tensor a) (tensor b) } + (*qed*) + + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/where/proof.json b/safety-related-profile/sonnx/ops/spec/formal/where/where/proof.json new file mode 100644 index 00000000..87e9eee2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/where/proof.json @@ -0,0 +1,33 @@ +{ + "profile": [ + { "prover": "z3@4.13.4", "size": 34, "time": 0.538 }, + { "prover": "cvc5@1.2.1", "size": 50, "time": 0.537 }, + { "prover": "alt-ergo@2.5.4", "size": 17, "time": 0.525 } + ], + "proofs": { + "CTensorWhere": { + "ctensor_where": { + "tactic": "split_vc", + "children": [ + { "prover": "alt-ergo@2.5.4", "time": 0.015 }, + { "prover": "alt-ergo@2.5.4", "time": 0.019 }, + { "prover": "alt-ergo@2.5.4", "time": 0.013 }, + { "prover": "alt-ergo@2.5.4", "time": 0.041 }, + { "prover": "alt-ergo@2.5.4", "time": 0.042 }, + { "prover": "alt-ergo@2.5.4", "time": 0.044 }, + { "prover": "alt-ergo@2.5.4", "time": 0.014 }, + { "prover": "alt-ergo@2.5.4", "time": 0.028 }, + { "prover": "alt-ergo@2.5.4", "time": 0.052 }, + { "prover": "alt-ergo@2.5.4", "time": 0.167 }, + { "prover": "alt-ergo@2.5.4", "time": 0.018 }, + { "prover": "alt-ergo@2.5.4", "time": 0.022 } + ] + } + }, + "OPWhere": { + "dwhere": { "prover": "alt-ergo@2.5.4", "time": 0.01 }, + "opwhere": { "prover": "alt-ergo@2.5.4", "time": 0.012 } + }, + "Where": {} + } +} diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/where/why3session.xml.bak b/safety-related-profile/sonnx/ops/spec/formal/where/where/why3session.xml.bak new file mode 100644 index 00000000..55b22b45 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/where/why3session.xml.bak @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/where/why3shapes.gz.bak b/safety-related-profile/sonnx/ops/spec/formal/where/where/why3shapes.gz.bak new file mode 100644 index 00000000..df1c0837 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/formal/where/where/why3shapes.gz.bak differ diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/where_driver.drv b/safety-related-profile/sonnx/ops/spec/formal/where/where_driver.drv new file mode 100644 index 00000000..95ca7526 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/where_driver.drv @@ -0,0 +1,32 @@ +module tensor.std.Clib + interface export "#include " + interface export "#include " + syntax val to_uint32 "(uint32_t) %1" + syntax val is_null "!%1" prec 0 + syntax val ([]) "%1[%2]" prec 1 1 15 + syntax val ([]<-) "%1[%2] = %3" prec 14 14 15 14 +end + +module tensor.std.Cfloat + syntax type float "double" + syntax val zero "0.0" + syntax val one "1.0" + syntax val f32 "(double) %1" + syntax val f64 "(double) %1" + syntax val ( .+ ) "%1 + %2" prec 8 8 7 + syntax val ( .- ) "%1 - %2" prec 8 8 7 + syntax val (.-_) "- %1" prec 5 4 + syntax val ( .* ) "%1 * %2" prec 7 7 6 + syntax val ( ./ ) "%1 / %2" prec 7 7 6 + syntax val ( .= ) "%1 = %2" prec 11 11 10 + syntax val ( .< ) "%1 < %2" prec 11 11 10 + syntax val ( .<= ) "%1 <= %2" prec 11 11 10 +end + +module where.CTensorWhere + interface "#include " + interface "#include " + interface "#include \"cindex.h\"" + interface "#include \"ctensor.h\"" + +end \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/formal/where/why3find.json b/safety-related-profile/sonnx/ops/spec/formal/where/why3find.json new file mode 100644 index 00000000..fb0612dc --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/formal/where/why3find.json @@ -0,0 +1,10 @@ +{ + "fast": 0.2, + "time": 1.0, + "depth": 4, + "packages": [], + "provers": [ "alt-ergo@2.5.4", "z3@4.13.4", "cvc5@1.2.1" ], + "tactics": [ "split_vc", "inline_goal" ], + "drivers": [], + "warnoff": [] +} diff --git a/safety-related-profile/sonnx/ops/spec/informal/abs/abs.md b/safety-related-profile/sonnx/ops/spec/informal/abs/abs.md new file mode 100644 index 00000000..90091e64 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/abs/abs.md @@ -0,0 +1,256 @@ +# Contents + +- **Abs** operator for type [real](#real) +- **Abs** operator for types [bfloat16, float16, float, double](#float) +- **Abs** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Abs version 13](https://onnx.ai/onnx/operators/onnx__Abs.html). + + +# **Abs** (real) + +## Signature +Definition of operator $\text{Abs}$ signature: +$Y = \textbf{Abs}(X)$ + +where: +- $X$: Input tensor +- $Y$: Absolute value of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Abs** operator. + +## Function +[E_ABS_REAL_FUNC_010]
+The **Abs** operator computes the element-wise absolute value of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$Y[i] = |X[i]|$$ + +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 +```math +X = \begin{bmatrix} -2.1 & 3.4 & -7 \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 2.1 & 3.4 & 7 \end{bmatrix} +``` + +### Example 2 +```math +X = \begin{bmatrix} -1.123 & 0 \\ 4 & -5 \\ 2 & -3 \end{bmatrix} +``` +```math +Y = \begin{bmatrix} 1.123 & 0 \\ 4 & 5 \\ 2 & 3 \end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Abs** has no attribute. + +## Inputs + +### $\text{X}$: real + +Input tensor. + +#### Constraints + + - `[E_ABS_REAL_CONSTR_X_010]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: real + +Absolute value of tensor $X$ + +#### Constraints + + - `[E_ABS_REAL_CONSTR_Y_010]` Shape consistency + - Statement: See [constraint E_ABS_REAL_CONSTR_X_010](#E_ABS_REAL_CONSTR_X_010) on tensor X + + + +# **Abs** (float) +where float is in {float16, float, double} + +## Signature +Definition of operator $\text{Abs}$ signature: +$Y = \textbf{Abs}(X)$ + +where: +- $X$: Input tensor +- $Y$: Absolute value of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Abs** operator: + +## Function +[E_ABS_FLOAT_FUNC_010]
+The **Abs** operator computes the element-wise absolute value of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i] = \text{NaN} \\ +\text{+Inf} & \text{if } X[i] = \pm \text{Inf} \\ +\text{+0} & \text{if } X[i] = \pm \text{0} \\ +-X[i] & \text{if } X[i] \lt 0 \\ +X[i] & \text{otherwise} +\end{cases} +$$ +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 +```math +X = \begin{bmatrix} -2.1 & \text{-Inf} & \text{NaN} & -0 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 2.1 & \text{Inf} & \text{NaN} & +0 \end{bmatrix} +``` + +## Error conditions + +No particular error, the function returns $\text{NaN}$ only when the input is $\text{NaN}$ + +## Attributes + +Operator **Abs** has no attribute. + +## Inputs + +### $\text{X}$: float + +Input tensor. + +#### Constraints + + - `[E_ABS_FLOAT_CONSTR_X_010]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: float + +Absolute value of tensor $X$ + +#### Constraints + + - `[E_ABS_FLOAT_CONSTR_Y_010]` Shape consistency + - Statement: See [constraint E_ABS_FLOAT_CONSTR_X_010](#E_ABS_FLOAT_CONSTR_Y_010) on tensor X + + + +# **Abs** (uint) +where uint is in {uint8, uint16, uint32, uint64}. + +See specification for [real numbers](#real). + + + +# **Abs** (int) +where int is in {int8, int16, int32, int64}. + +## Signature +Definition of operator $\text{Abs}$ signature: +$Y = \textbf{Abs}(X)$ + +where: +- $X$: Input tensor +- $Y$: Absolute value of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Abs** operator: + +## Function +[E_ABS_INT_FUNC_010]
+The **Abs** operator computes the element-wise absolute value of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = +\begin{cases} +-X[i] & \text{if } X[i] \lt 0 \\ +X[i] & \text{otherwise} +\end{cases} +$$ +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 +```math +X = \begin{bmatrix} -126 & 0 & \text{127} \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 126 & \text{0} & \text{127} \end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Abs** has no attribute. + +## Inputs + +### $\text{X}$: int + +Input tensor. + +#### Constraints + + - `[E_ABS_INT_CONSTR_X_010]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + - `[E_ABS_INT_CONSTR_X_020]` No absolute value for minimum integer + - Statement: $X \neq \texttt{minint}$. + +## Outputs + +### $\text{Y}$: float + +Absolute value of tensor $X$ + +#### Constraints + + - `[E_ABS_INT_CONSTR_Y_010]` Shape consistency + - Statement: See [constraint E_ABS_INT_CONSTR_X_010](#E_ABS_INT_CONSTR_Y_010) on tensor X + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/abs/abs_acc.md b/safety-related-profile/sonnx/ops/spec/informal/abs/abs_acc.md new file mode 100644 index 00000000..ebafa34f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/abs/abs_acc.md @@ -0,0 +1,62 @@ +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Abs** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = |X|$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\text{abs}(x)$: + +- For $x > 0$: $\dfrac{d|x|}{dx} = 1$ +- For $x < 0$: $\dfrac{d|x|}{dx} = -1$ +- For $x = 0$: derivative is undefined, but the function is Lipschitz continuous with constant 1. + +A first-order bound is: + +- For every index $I$ such that $X[I] \neq 0$ and $X[I] + X_{\textit{err}}[I] \neq 0$ (no sign crossing through zero): + + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |X_{\textit{err}}[I]|$ + +- If $X[I]$ and $X[I] + X_{\textit{err}}[I]$ have different signs (crossing zero), the propagated error may still be bounded by the same Lipschitz constant, but the output may switch from $|x|$ to $|-x|$ and thus require the bound: + + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |X_{\textit{err}}[I]|$ + (This holds because Abs is 1-Lipschitz everywhere.) + +### Error Introduction + +Error introduction for real (ideal) arithmetic is null: + +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto result = [&X](auto I) { + // Real-domain abs + return abs(X[I]); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + + auto y = result(I); + + // First-order propagated error bound: |err_abs| <= |err_x| + double bound = std::abs(x.err); + + assert(std::abs(y.err) <= bound + 1e-12); +} +``` \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/abs/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/abs/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/abs/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/abs/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/abs/reviews/eric.md b/safety-related-profile/sonnx/ops/spec/informal/abs/reviews/eric.md new file mode 100644 index 00000000..74cda988 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/abs/reviews/eric.md @@ -0,0 +1 @@ +I have directly modified the original file. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/add/add.md b/safety-related-profile/sonnx/ops/spec/informal/add/add.md new file mode 100644 index 00000000..1839e306 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/add/add.md @@ -0,0 +1,322 @@ +# Contents +- **Add** operator for type [real](#real) +- **Add** operator for types [float16, float, double](#float) +- **Add** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Add version 14](https://onnx.ai/onnx/operators/onnx__Add.html#l-onnx-doc-add). + +--- + + +# **Add** (real, real) + +## Signature + +Definition of operator $\text{Add}$ signature: + +$C = \text{Add}(A, B)$ + +where: +- $A$: first operand of the addition +- $B$: second operand of the addition +- $C$: result of the element-wise addition of $A$ to $B$ + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Add** operator. + +## Function + +[E_ADD_REAL_FUNC_0010]
+Operator **Add** adds input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. If $i$ is a [tensor index](./../common/definitions.md#tensor_index), each element $C[i]$ is the result of adding $A[i]$ and $B[i]$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = A[i] + B[i] +$$ + +[END]
+ + +The effect of the operator is illustrated on the following examples: + +--- + +### Example 1 (1D tensors) + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 2 & 3 & 4 \end{bmatrix} +``` + +```math +C = A + B = \begin{bmatrix} 8.1 & 12.5 & 39.7 \end{bmatrix} +``` + +--- + +## Error conditions +No error condition. + +## Attributes +Operator **Add** has no attribute. + +## Inputs + +### $\text{A}$: real tensor +Tensor $A$ is the first operand of the addition. + +#### Constraints + + - `[E_ADD_REAL_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + + +### $\text{B}$: real tensor +Tensor $B$ is the second operand of the addition. + +#### Constraints + + - `[E_ADD_REAL_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_ADD_REAL_CONSTR_A_0010](#E_ADD_REAL_CONSTR_A_0010) on tensor $A$. + + +## Outputs + +### $\text{C}$: real tensor + +Tensor $C$ is the result of the element-wise addition of $A$ and $B$. + +#### Constraints + + - `[E_ADD_REAL_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_ADD_REAL_CONSTR_A_0010](#E_ADD_REAL_CONSTR_A_0010) on tensor $A$. + + + +# **Add** (float, float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Add}$ signature: + +$C = \text{Add}(A, B)$ + +where + + - $A$: first operand tensor + - $B$: second operand tensor + - $C$: output tensor, result of element-wise addition of $A$ to $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Add** operator. + + +## Function + +[E_ADD_FLOAT_FUNC_0010]
+Operator **Add** adds input tensors $A$ and $B$ element-wise according to IEEE 754 floating-point semantics and stores the result in output tensor $C$. If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $C[i]$ is the result of adding $A[i]$ and $B[i]$ + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = A[i] + B[i] +$$ + +[END]
+ +### Example 1 (2D tensors) + +```math +A = \begin{bmatrix} 3.0 & 4.5 \\ 16.0 & 1.0 \\ 25.5 & 24.25 \end{bmatrix} +\quad +B = \begin{bmatrix} 3.0 & 2.0 \\ 4.0 & 0.0 \\ 5.0 & 4.0 \end{bmatrix} +``` + +```math +C = A + B = \begin{bmatrix} 6.0 & 6.5 \\ 20.0 & 1.0 \\ 30.5 & 28.25 \end{bmatrix} +``` +## Error conditions +No error condition. + +## Attributes + +Operator **Add** has no attribute. + +## Inputs + +### $\text{A}$: floating-point tensor +Tensor $A$ is the first operand of the addition. + +#### Constraints + +- `[E_ADD_FLOAT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_ADD_FLOAT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: floating-point tensor +Tensor $B$ is the second operand of the addition. + +#### Constraints +- `[E_ADD_FLOAT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_ADD_FLOAT_CONSTR_A_0010](#E_ADD_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_ADD_FLOAT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_ADD_FLOAT_CONSTR_A_0020](#E_ADD_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: floating-point tensor + +Tensor $C$ is the result of the element-wise floating-point addition of $A$ and $B$. + +#### Constraints + + - `[E_ADD_FLOAT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_ADD_FLOAT_CONSTR_A_0010](#E_ADD_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_ADD_FLOAT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_ADD_FLOAT_CONSTR_A_0020](#E_ADD_FLOAT_CONSTR_A_0020) on tensor $A$. + + + +# **Add** (int, int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +## Signature +Definition of operator $\text{Add}$ signature: + + $C = \text{Add}(A,B)$ + + where + - $A$: first operand of the addition + - $B$: second operand of the addition + - $C$: result of the element-wise addition of $A$ to $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Add** operator. + +## Function + +[E_ADD_INT_FUNC_0010]
+Operator **Add** adds input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. Each element $C[i]$ is the result of adding $A[i]$ and $B[i]$ where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The integer addition is performed as follows (considering that all tensors have the same type): + +For unsigned values (type uint\): + +$$ +C[i] = +\begin{cases} + A[i] + B[i] - 2^{n} & \text{if } A[i] + B[i] > 2^{n}-1 \\ + A[i] + B[i] & \text{otherwise} +\end{cases} +$$ + +For signed values (type int\): + +$$ +C[i] = +\begin{cases} + A[i] + B[i] - 2^{n} & \text{if } A[i] + B[i] > 2^{n-1}-1 \\ + A[i] + B[i] + 2^{n} & \text{if } A[i] + B[i] < -2^{n-1} \\ + A[i] + B[i] & \text{otherwise} +\end{cases} +$$ + +[END]
+ +### Example 1 (1D uint8 tensors) + +```math +A = \begin{bmatrix} 6 & 200 & 35 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 100 & 5 \end{bmatrix} +``` +```math +C = \begin{bmatrix} 9 & 44 & 40 \end{bmatrix} +``` + +### Example 2 (1D int8 tensors) + +```math +A = \begin{bmatrix} -6 & 100 & -100 \end{bmatrix} +\quad +B = \begin{bmatrix} -3 & 100 & -100 \end{bmatrix} +``` +```math +C = \begin{bmatrix} -9 & -56 & 56 \end{bmatrix} +``` + +## Error conditions +- According to the definition, the result of the addition differs from the value that would be expected in $\mathbb{N}$ (for unsigned) or $\mathbb{Z}$ (for signed) when under- or overflow occur. + +## Attributes + +The **Add** operator has no attribute. + +## Inputs + +### $\text{A}$: integer tensor + +Tensor $A$ is the first operand of the addition. + +#### Constraints + +- `[E_ADD_INT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_ADD_INT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + + + +### $\text{B}$: `integer tensor` + +Tensor $B$ is the second operand of the addition. + +#### Constraints + +- `[E_ADD_INT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_ADD_INT_CONSTR_A_0010](#E_ADD_INT_CONSTR_A_0010) on tensor $A$. +- `[E_ADD_INT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_ADD_INT_CONSTR_A_0020](#E_ADD_INT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: `integer tensor` + +Tensor $C$ is the result of the element-wise integer addition of $A$ and $B$. + +#### Constraints + + - `[E_ADD_INT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_ADD_INT_CONSTR_A_0010](#E_ADD_INT_CONSTR_A_0010) on tensor $A$. +- `[E_ADD_INT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_ADD_INT_CONSTR_A_0020](#E_ADD_INT_CONSTR_A_0020) on tensor $A$. + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/add/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/add/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/add/reviews/jean.md b/safety-related-profile/sonnx/ops/spec/informal/add/reviews/jean.md new file mode 100644 index 00000000..f0799dae --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/add/reviews/jean.md @@ -0,0 +1,13 @@ +Rmk1: Typos and reformatting, already taken into account in add.md + +Rmk2: Contents section. + +* ONNX bfloat16 type is in the ONNX description of add but not in SONNX add.md. If this type is never used in SONNX, it has to be inserted as a general restriction. + +* INT4 and UINT4 are in SONNX add.md but not in the ONNX description of add. + +* Add reference to the ONNX documentation. Taken into account in add.md. + +Rmk3: broken links. Already fixed. + +Rmk4: Replace the type names by their counterpart in ONNX. Example: replace "FP64" by "double". Taken into account in add.md. diff --git a/safety-related-profile/sonnx/ops/spec/informal/add/reviews/rev1.md b/safety-related-profile/sonnx/ops/spec/informal/add/reviews/rev1.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/argmax/argmax.md b/safety-related-profile/sonnx/ops/spec/informal/argmax/argmax.md new file mode 100644 index 00000000..671d540a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/argmax/argmax.md @@ -0,0 +1,1306 @@ +# Contents + +* **ArgMax** operator for type [real](#real) +* **ArgMax** operator for types [float16, float, double](#float) +* **ArgMax** operator for integer types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#integer) + +Based on ONNX documentation [ArgMax version 13](https://onnx.ai/onnx/operators/onnx__ArgMax.html). + + + +# **ArgMax** (real) + +## Signature + +$Y = \textbf{ArgMax}(X)$ + +where: + +* $X$: input tensor +* $Y$: output tensor containing the indices of the maximum values of $X$ along a given axis + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +| Restriction | Statement | Origin | +| ---------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------ | +| `[R1]` | Attribute `axis` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | Attribute `keepdims` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Attribute `select_last_index` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R4]` | `axis` $\ge 0$ | Transient | +| `[R5]` | The dimension of $X$ along `axis` must be strictly positive | Mathematical definition | + +--- + +## Informal specification + +The **ArgMax** operator computes the index of the maximum value of the input tensor $X$ along a specified axis. + +Let: + +$$ +r = \operatorname{rank}(X) +$$ + +where $r$ is the rank of the input tensor $X$. + +Let `axis` be the dimension along which the maximum value is searched. + +Let: + +$$ +dX_k +$$ + +be the size of tensor $X$ along dimension $k$. + +The size of the reduced dimension is: + +$$ +dX_{\mathrm{axis}} +$$ + +For every output position $k$, all indices of $X$ are fixed except the index along the `axis` dimension. + +This varying index is denoted by $j$. + +It satisfies: + +$$ +0 \leq j < dX_{\mathrm{axis}} +$$ + +For a fixed output position $k$, let: + +$$ +S_k(j) +$$ + +denote the value of $X$ at the same fixed coordinates as $k$, with the coordinate $j$ inserted along the `axis` dimension. + +In other words, $S_k(j)$ represents the slice of $X$ being inspected by **ArgMax** for one output position. + +The maximum value of this slice is: + +$$ +M_k = +\max_{0 \leq j < dX_{\mathrm{axis}}} +S_k(j) +$$ + +The output value $Y[k]$ is an index $j$ where this maximum value is reached. + +If `select_last_index = 0`, the first occurrence of the maximum value is selected. + +In other words, $Y[k]$ is the smallest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +If `select_last_index = 1`, the last occurrence of the maximum value is selected. + +In other words, $Y[k]$ is the greatest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +The output tensor contains integer indices in the range: + +$$ +0 \leq Y[k] < dX_{\mathrm{axis}} +$$ + +The shape of the output tensor depends on the value of `keepdims`. + +If `keepdims = 1`, the output tensor keeps the same rank as $X$, and the reduced dimension is replaced by $1$. + +If `keepdims = 0`, the reduced dimension is removed from the output tensor. + +--- + +### Example 1: 1D tensor + +Let: + +$$ +X = +\begin{bmatrix} +1 & 5 & 3 & 2 +\end{bmatrix} +$$ + +with: + +$$ +axis = 0 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum value is $5$. + +It is located at index $1$. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 +\end{bmatrix} +$$ + +The shape of $X$ is $(4)$. + +The shape of $Y$ is $(1)$. + +--- + +### Example 2: 2D tensor with `axis = 0` + +Let: + +$$ +X = +\begin{bmatrix} +1 & 9 & 3 \ +4 & 2 & 6 +\end{bmatrix} +$$ + +with: + +$$ +axis = 0 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed column by column. + +For column $0$: + +$$ +\max(1,4)=4 +$$ + +The selected index is $1$. + +For column $1$: + +$$ +\max(9,2)=9 +$$ + +The selected index is $0$. + +For column $2$: + +$$ +\max(3,6)=6 +$$ + +The selected index is $1$. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 & 0 & 1 +\end{bmatrix} +$$ + +The shape of $X$ is $(2,3)$. + +The shape of $Y$ is $(1,3)$. + +--- + +### Example 3: 2D tensor with `axis = 1` + +Let: + +$$ +X = +\begin{bmatrix} +1 & 9 & 3 \ +4 & 2 & 6 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed row by row. + +For row $0$: + +$$ +\max(1,9,3)=9 +$$ + +The selected index is $1$. + +For row $1$: + +$$ +\max(4,2,6)=6 +$$ + +The selected index is $2$. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +The shape of $X$ is $(2,3)$. + +The shape of $Y$ is $(2,1)$. + +--- + +### Example 4: `keepdims = 0` + +Let: + +$$ +X = +\begin{bmatrix} +1 & 9 & 3 \ +4 & 2 & 6 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 0 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed row by row. + +The reduced dimension is removed. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 & 2 +\end{bmatrix} +$$ + +The shape of $X$ is $(2,3)$. + +The shape of $Y$ is $(2)$. + +--- + +### Example 5: repeated maximum with first index selected + +Let: + +$$ +X = +\begin{bmatrix} +1 & 5 & 5 & 2 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum value is $5$. + +It appears at indices $1$ and $2$ along the reduced axis. + +Since `select_last_index = 0`, the first occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 +\end{bmatrix} +$$ + +--- + +### Example 6: repeated maximum with last index selected + +Let: + +$$ +X = +\begin{bmatrix} +1 & 5 & 5 & 2 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 1 +$$ + +The maximum value is $5$. + +It appears at indices $1$ and $2$ along the reduced axis. + +Since `select_last_index = 1`, the last occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +2 +\end{bmatrix} +$$ + +--- + +### Example 7: 3D tensor + +Let: + +$$ +X = +\begin{bmatrix} +\begin{bmatrix} +1 & 3 & 2 \ +4 & 0 & 6 +\end{bmatrix} +\ +\begin{bmatrix} +7 & 5 & 9 \ +2 & 8 & 1 +\end{bmatrix} +\end{bmatrix} +$$ + +The shape of $X$ is: + +$$ +shape(X) = (2,2,3) +$$ + +with: + +$$ +axis = 2 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed along the last dimension. + +For the first inner matrix: + +$$ +\begin{bmatrix} +1 & 3 & 2 \ +4 & 0 & 6 +\end{bmatrix} +$$ + +the selected indices are: + +$$ +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +For the second inner matrix: + +$$ +\begin{bmatrix} +7 & 5 & 9 \ +2 & 8 & 1 +\end{bmatrix} +$$ + +the selected indices are: + +$$ +\begin{bmatrix} +2 \ +1 +\end{bmatrix} +$$ + +Therefore: + +$$ +Y = +\begin{bmatrix} +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +\ +\begin{bmatrix} +2 \ +1 +\end{bmatrix} +\end{bmatrix} +$$ + +The shape of $Y$ is: + +$$ +shape(Y) = (2,2,1) +$$ + +--- + +## Error conditions + +The operator is undefined if one of the following conditions holds: + +* `axis < 0` +* `axis >= r`, where $r$ is the rank of $X$ +* $dX_{\mathrm{axis}} = 0$ +* `keepdims` is not equal to `0` or `1` +* `select_last_index` is not equal to `0` or `1` + +## Attributes + +### `axis`: int + +Specifies the dimension along which the maximum index is computed. + +#### Constraints + +* `[C1]` Value domain + + * Statement: `axis >= 0`. `[R4]` + +* `[C2]` Consistency with tensor rank + + * Statement: `axis < r`, where $r$ is the rank of $X$. + +### `keepdims`: int + +Specifies whether the reduced dimension is kept in the output tensor. + +If `keepdims = 1`, the reduced dimension is kept with size $1$. + +If `keepdims = 0`, the reduced dimension is removed. + +#### Constraints + +* `[C1]` Value domain + + * Statement: `keepdims` must be equal to `0` or `1`. + +### `select_last_index`: int + +Specifies how ties are resolved when the maximum value appears more than once along the reduced axis. + +If `select_last_index = 0`, the first index of the maximum value is selected. + +If `select_last_index = 1`, the last index of the maximum value is selected. + +#### Constraints + +* `[C1]` Value domain + + * Statement: `select_last_index` must be equal to `0` or `1`. + +## Inputs + +### $\text{X}$: real tensor + +Input tensor. + +#### Constraints + +* `[C1]` Axis consistency + + * Statement: The rank $r$ of $X$ shall satisfy `axis < r`. + +* `[C2]` Non-empty reduced dimension + + * Statement: The dimension of $X$ along `axis` shall be strictly positive. + +## Outputs + +### $\text{Y}$: int64 tensor + +Output tensor containing the indices of the maximum values of $X$ along `axis`. + +#### Constraints + +* `[C1]` Output type + + * Statement: $Y$ shall have type `int64`. + +* `[C2]` Output shape consistency + + * Statement: If `keepdims = 1`, the shape of $Y$ shall be equal to the shape of $X$, except that the dimension `axis` shall be replaced by $1$. + + * Statement: If `keepdims = 0`, the shape of $Y$ shall be equal to the shape of $X$ with the dimension `axis` removed. + +* `[C3]` Output value range + + * Statement: For every output index $k$: + +$$ +0 \leq Y[k] < dX_{\mathrm{axis}} +$$ + + + +# **ArgMax** (float) + +where float is in {float16, float, double}. + +## Signature + +$Y = \textbf{ArgMax}(X)$ + +where: + +* $X$: floating-point input tensor +* $Y$: int64 output tensor containing the indices of the maximum values of $X$ along a given axis + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +See [Restrictions](#real). + +--- + +## Informal specification + +The **ArgMax** operator computes the index of the maximum floating-point value of the input tensor $X$ along a specified axis according to IEEE 754 floating-point semantics. + +The output tensor contains integer indices and not floating-point values. + +For finite values, the behavior is the same as for real numbers. + +For every output position $k$, all indices of $X$ are fixed except the index along the `axis` dimension. + +This varying index is denoted by $j$. + +It satisfies: + +$$ +0 \leq j < dX_{\mathrm{axis}} +$$ + +For a fixed output position $k$, let: + +$$ +S_k(j) +$$ + +denote the value of $X$ at the same fixed coordinates as $k$, with the coordinate $j$ inserted along the `axis` dimension. + +If the reduced slice contains no `NaN`, then: + +* `+inf` is greater than all finite values. +* Finite values are ordered according to the usual floating-point order. +* `-inf` is smaller than all finite values. +* If the maximum appears several times, the selected index depends on `select_last_index`. + +If the reduced slice contains one or more `NaN` values, the result is defined deterministically as follows: + +* if `select_last_index = 0`, the index of the first `NaN` along the reduced axis is returned; +* if `select_last_index = 1`, the index of the last `NaN` along the reduced axis is returned. + +This convention gives priority to `NaN` values over finite and infinite values in the reduced slice. + +If the reduced slice contains at least one `NaN`, then `NaN` values have priority over all other values. + +If `select_last_index = 0`, the selected output value is the first index of a `NaN` value along the reduced axis. + +In other words, $Y[k]$ is the smallest index $j$ such that: + +$$ +S_k(j) = \text{NaN} +$$ + +If `select_last_index = 1`, the selected output value is the last index of a `NaN` value along the reduced axis. + +In other words, $Y[k]$ is the greatest index $j$ such that: + +$$ +S_k(j) = \text{NaN} +$$ + +If the reduced slice contains no `NaN`, then the maximum value is computed normally. + +Let: + +$$ +M_k = +\max_{0 \leq j < dX_{\mathrm{axis}}} +S_k(j) +$$ + +If `select_last_index = 0`, the selected output value is the first index where the maximum value is reached. + +In other words, $Y[k]$ is the smallest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +If `select_last_index = 1`, the selected output value is the last index where the maximum value is reached. + +In other words, $Y[k]$ is the greatest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +The output shape is defined in the same way as in [ArgMax (real)](#real). + +--- + +### Example 1: finite floating-point values + +Let: + +$$ +X = +\begin{bmatrix} +1.0 & 9.5 & 3.0 \ +4.0 & 2.0 & 6.5 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed row by row. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +--- + +### Example 2: `+inf` + +Let: + +$$ +X = +\begin{bmatrix} +1.0 & +\inf & 3.0 \ +4.0 & 2.0 & 6.5 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +For the first row, `+inf` is the maximum value, located at index $1$. + +For the second row, $6.5$ is the maximum value, located at index $2$. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +--- + +### Example 3: `-inf` + +Let: + +$$ +X = +\begin{bmatrix} +-\inf & -4.0 & -2.0 \ +-\inf & -\inf & -\inf +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +For the first row, $-2.0$ is the maximum value, located at index $2$. + +For the second row, all values are equal to $-\inf$. + +Since `select_last_index = 0`, the first occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +2 \ +0 +\end{bmatrix} +$$ + +--- + +### Example 4: `NaN` with first index selected + +Let: + +$$ +X = +\begin{bmatrix} +1.0 & \text{NaN} & 3.0 \ +4.0 & 2.0 & 6.5 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The first row contains `NaN` at index $1$. + +Since `select_last_index = 0`, the first `NaN` index is selected. + +The second row contains no `NaN`, so the maximum value $6.5$ is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +--- + +### Example 5: `NaN` with last index selected + +Let: + +$$ +X = +\begin{bmatrix} +\text{NaN} & 1.0 & \text{NaN} & 3.0 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 1 +$$ + +The row contains two `NaN` values at indices $0$ and $2$. + +Since `select_last_index = 1`, the last `NaN` index is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +2 +\end{bmatrix} +$$ + +--- + +### Example 6: repeated maximum with floating-point values + +Let: + +$$ +X = +\begin{bmatrix} +1.0 & 7.0 & 7.0 & 2.0 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum value is $7.0$. + +It appears at indices $1$ and $2$. + +Since `select_last_index = 0`, the first occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 +\end{bmatrix} +$$ + +With the same input and: + +$$ +select_last_index = 1 +$$ + +the last occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +2 +\end{bmatrix} +$$ + +--- + +## Error conditions + +The operator is undefined if one of the following conditions holds: + +* `axis < 0` +* `axis >= r`, where $r$ is the rank of $X$ +* $dX_{\mathrm{axis}} = 0$ +* `keepdims` is not equal to `0` or `1` +* `select_last_index` is not equal to `0` or `1` + +The operator does not return `NaN`, because the output tensor contains integer indices. + +## Attributes + +### `axis`: int + +See [ArgMax (real)](#real). + +### `keepdims`: int + +See [ArgMax (real)](#real). + +### `select_last_index`: int + +See [ArgMax (real)](#real). + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +* `[C1]` Axis consistency + + * Statement: The rank $r$ of $X$ shall satisfy `axis < r`. + +* `[C2]` Non-empty reduced dimension + + * Statement: The dimension of $X$ along `axis` shall be strictly positive. + +## Outputs + +### $\text{Y}$: int64 tensor + +Output tensor containing the indices of the maximum values of $X$ along `axis`. + +#### Constraints + +* `[C1]` Output type + + * Statement: $Y$ shall have type `int64`. + +* `[C2]` Output shape consistency + + * Statement: See constraint [C2](#C2ry) on tensor $Y$ in [ArgMax (real)](#real). + +* `[C3]` Output value range + + * Statement: See constraint [C3](#C3ry) on tensor $Y$ in [ArgMax (real)](#real). + +## Numeric accuracy + +No numeric accuracy note is required for the output, since **ArgMax** returns integer indices. + +The only floating-point aspects concern value comparison along the reduced axis, including the handling of `NaN`, `+inf`, and `-inf`. + + + +# **ArgMax** (integer) + +where integer is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +## Signature + +$Y = \textbf{ArgMax}(X)$ + +where: + +* $X$: integer input tensor +* $Y$: int64 output tensor containing the indices of the maximum values of $X$ along a given axis + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +See [Restrictions](#real). + +--- + +## Informal specification + +The **ArgMax** operator computes the index of the maximum integer value of the input tensor $X$ along a specified axis. + +For every output position $k$, all indices of $X$ are fixed except the index along the `axis` dimension. + +This varying index is denoted by $j$. + +It satisfies: + +$$ +0 \leq j < dX_{\mathrm{axis}} +$$ + +For a fixed output position $k$, let: + +$$ +S_k(j) +$$ + +denote the value of $X$ at the same fixed coordinates as $k$, with the coordinate $j$ inserted along the `axis` dimension. + +The maximum value of this slice is: + +$$ +M_k = +\max_{0 \leq j < dX_{\mathrm{axis}}} +S_k(j) +$$ + +If `select_last_index = 0`, the first occurrence of the maximum value is selected. + +In other words, $Y[k]$ is the smallest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +If `select_last_index = 1`, the last occurrence of the maximum value is selected. + +In other words, $Y[k]$ is the greatest index $j$ such that: + +$$ +S_k(j) = M_k +$$ + +The output tensor contains integer indices of type `int64`. + +The output shape is defined in the same way as in [ArgMax (real)](#real). + +--- + +### Example 1: integer tensor + +Let: + +$$ +X = +\begin{bmatrix} +1 & 9 & 3 \ +4 & 2 & 6 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum is computed row by row. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 \ +2 +\end{bmatrix} +$$ + +--- + +### Example 2: repeated maximum + +Let: + +$$ +X = +\begin{bmatrix} +2 & 8 & 8 & 1 +\end{bmatrix} +$$ + +with: + +$$ +axis = 1 +$$ + +$$ +keepdims = 1 +$$ + +$$ +select_last_index = 0 +$$ + +The maximum value is $8$. + +It appears at indices $1$ and $2$. + +Since `select_last_index = 0`, the first occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +1 +\end{bmatrix} +$$ + +With the same input and: + +$$ +select_last_index = 1 +$$ + +the last occurrence is selected. + +Therefore: + +$$ +Y = +\begin{bmatrix} +2 +\end{bmatrix} +$$ + +--- + +## Error conditions + +The operator is undefined if one of the following conditions holds: + +* `axis < 0` +* `axis >= r`, where $r$ is the rank of $X$ +* $dX_{\mathrm{axis}} = 0$ +* `keepdims` is not equal to `0` or `1` +* `select_last_index` is not equal to `0` or `1` + +## Attributes + +### `axis`: int + +See [ArgMax (real)](#real). + +### `keepdims`: int + +See [ArgMax (real)](#real). + +### `select_last_index`: int + +See [ArgMax (real)](#real). + +## Inputs + +### $\text{X}$: integer tensor + +Input tensor. + +#### Constraints + +* `[C1]` Axis consistency + + * Statement: The rank $r$ of $X$ shall satisfy `axis < r`. + +* `[C2]` Non-empty reduced dimension + + * Statement: The dimension of $X$ along `axis` shall be strictly positive. + +## Outputs + +### $\text{Y}$: int64 tensor + +Output tensor containing the indices of the maximum values of $X$ along `axis`. + +#### Constraints + +* `[C1]` Output type + + * Statement: $Y$ shall have type `int64`. + +* `[C2]` Output shape consistency + + * Statement: See constraint [C2](#C2ry) on tensor $Y$ in [ArgMax (real)](#real). + +* `[C3]` Output value range + + * Statement: See constraint [C3](#C3ry) on tensor $Y$ in [ArgMax (real)](#real). + +## Numeric accuracy + +No numeric accuracy note is required, since **ArgMax** returns integer indices. diff --git a/safety-related-profile/sonnx/ops/spec/informal/batchnormalization/batch_normalization.md b/safety-related-profile/sonnx/ops/spec/informal/batchnormalization/batch_normalization.md new file mode 100644 index 00000000..5e407a32 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/batchnormalization/batch_normalization.md @@ -0,0 +1,433 @@ +# Contents + +- **BatchNormalization** operator for type [real](#real) +- **BatchNormalization** operator for types [float16, float, double](#float) + +Based on ONNX documentation [BatchNormalization version 15](https://onnx.ai/onnx/operators/onnx__BatchNormalization.html). + + +# **BatchNormalization** (real) + +## Signature +$Y = \textbf{BatchNormalization}(X, scale,B,input\_mean,input\_var)$ + +where: +- $X$: Input tensor to be normalized +- $scale$: per-channel scaling factor $\gamma$ +- $B$: per-channel bias $\beta$ +- $input\_mean$: per-channel mean used for normalization +- $input\_var$: per-channel variance used for normalization +- $Y$: normalized, scaled, and shifted output tensor + + +## Restrictions +[General restrictions](../common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **BatchNormalization** operator: +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Only inference mode is specified (training_mode = 0). Training mode is out of scope. | Transient | + + +## Informal specification + +Operator **BatchNormalization** normalizes the input tensor $X$ along the channel axis using the pre-computed per-channel statistics $input\_mean$ +and $input\_var$, then applies a per-channel affine transformation using $scale$ and bias $B$ +and stores the result in output tensor $Y$. + +The tensor $X$ has shape $(N, C, d_2, \ldots, d_{rX-1})$ where $N = dX_0$ +is the batch size and $C = dX_1$ +is the number of channels. When $X$ is a $1-D$ tensor of size $N$, the number of channels $C$ is implicitly assumed to be $1$. + +The parameters $scale$, $B$,$input\_mean$, and +$input\_var$ are $1-D$ tensors of size $C$, each indexed by the channel index $c \in [0, C-1]$ + +For any element of the input tensor, the output is computed as follows: +$Y[n, c, d_2, \ldots] = \text{scale}[c] \cdot \frac{X[n, c, d_2, \ldots] - \text{input\_mean}[c]}{\sqrt{\text{input\_var}[c] + \varepsilon}} + B[c]$ + +where: +- $n \in [0,N−1]$ is the batch index +- $c \in [0, C-1]$ is the channel index +- $d_k​ \in [0,dX_k​−1]$ for $k \in [2,rX−1]$ are the spatial indices +- $\epsilon$ is the value of attribute epsilon + +### Examples + +#### Example 1 + +Single-channel 1-D input ($N=1$, $C=1$, scalar parameters): + +```math +X = \begin{bmatrix} 2.0 & 4.0 & 6.0 \end{bmatrix} +\quad +input\_mean = \begin{bmatrix} 4.0 \end{bmatrix} +\quad +input\_var = \begin{bmatrix} 2.0 \end{bmatrix} +``` + +```math +scale = \begin{bmatrix} 1.0 \end{bmatrix} +\quad +B = \begin{bmatrix} 0.0 \end{bmatrix} +\quad +\varepsilon = 10^{-5} +``` + +```math +Y = \begin{bmatrix} + \frac{2.0 - 4.0}{\sqrt{2.0 + 10^{-5}}} & + \frac{4.0 - 4.0}{\sqrt{2.0 + 10^{-5}}} & + \frac{6.0 - 4.0}{\sqrt{2.0 + 10^{-5}}} +\end{bmatrix} +\approx +\begin{bmatrix} -1.4142 & 0.0 & 1.4142 \end{bmatrix} +``` + +#### Example 2 + +Two-channel 2-D input ($N=2$, $C=2$, spatial dimension $H=2$): + +```math +X = \begin{bmatrix} + [[1.0,\ 2.0],\ [3.0,\ 4.0]] \\ + [[5.0,\ 6.0],\ [7.0,\ 8.0]] +\end{bmatrix} +\quad\text{(shape: } 2 \times 2 \times 2 \text{)} +``` + +```math +input\_mean = \begin{bmatrix} 2.0 \\ 6.0 \end{bmatrix} +\quad +input\_var = \begin{bmatrix} 1.0 \\ 1.0 \end{bmatrix} +\quad +scale = \begin{bmatrix} 2.0 \\ 1.0 \end{bmatrix} +\quad +B = \begin{bmatrix} 0.5 \\ -0.5 \end{bmatrix} +\quad +\varepsilon = 10^{-5} +``` + +For channel $c=0$ (mean = 2.0, var = 1.0, scale = 2.0, B = 0.5): + +```math +Y[\cdot,0,\cdot] \approx \begin{bmatrix} -1.5 & 0.5 \\ 2.5 & 4.5 \end{bmatrix} +``` + +For channel $c=1$ (mean = 6.0, var = 1.0, scale = 1.0, B = −0.5): + +```math +Y[\cdot,1,\cdot] \approx \begin{bmatrix} -1.5 & -0.5 \\ 0.5 & 1.5 \end{bmatrix} +``` + + +## Error conditions + +No error condition applies in inference mode (`training_mode = 0`). In particular, division by zero is prevented by the `epsilon` attribute, which ensures $input\_var[c] + \varepsilon > 0$ for any non-negative variance. + + +## Attributes + +### `epsilon`: float + +A small positive value added to the variance before taking the square root, to avoid division by zero. + +Default value: $10^{-5}$ + +#### Constraints + +- `[C1]` Positivity + - Statement: `epsilon` shall be strictly positive: $\varepsilon > 0$. +### `momentum`: float + +Factor used when computing running mean and running variance during training. This attribute is irrelevant in inference mode ([R1](#R1)) and shall be ignored. + +Default value: $0.9$ + +#### Constraints + +No constraint applies to `momentum` in inference mode. + +### `training_mode`: int + +Flag indicating whether the operator is used in training mode. Per restriction [R1](#R1), only inference mode is in scope. + +Default value: $0$ (inference) + +#### Constraints + +- `[C1]` Inference mode only + - Statement: `training_mode` shall be $0$. +## Inputs + +### $X$: real tensor + +Input tensor to be normalized. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ shall have the same shape. +- `[C2]` Rank + - Statement: $X$ shall have rank $rX \geq 1$. When $rX = 1$, the number of channels $C$ is implicitly 1. +### $scale$: real tensor + +Per-channel scaling factor $\gamma$, applied after normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: $scale$ shall be a 1-D tensor of size $C = dX_1$ (or size 1 when $rX = 1$). +### $B$: real tensor + +Per-channel bias $\beta$, added after scaling. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_r) on $scale$. +### $input\_mean$: real tensor + +Per-channel estimated mean used for normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_r) on $scale$. +### $input\_var$: real tensor + +Per-channel estimated variance used for normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_r) on $scale$. +- `[C2]` Non-negativity + - Statement: $\forall c,\ input\_var[c] \geq 0$. +## Outputs + +### $Y$: real tensor + +Output tensor, result of the normalization and affine transformation applied to $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: see constraint [**[C1]**](#C1X_r) on $X$. +## Formal specification + +See the Why3 specification. + +--- + + +# **BatchNormalization** (float) +where float is in {float16, float, double} + +## Signature + +$Y = \textbf{BatchNormalization}(X, scale, B, input\_mean, input\_var)$ + +where: +- $X$: input floating-point tensor to be normalized +- $scale$: per-channel scaling factor $\gamma$ +- $B$: per-channel bias $\beta$ +- $input\_mean$: per-channel estimated mean +- $input\_var$: per-channel estimated variance +- $Y$: normalized, scaled, and shifted output tensor +## Restrictions + +[General restrictions](../common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **BatchNormalization** operator: + +| Restriction | Statement | Origin | +|-------------|-----------|--------| +| `[R1]` | Only inference mode is specified (`training_mode = 0`). Training mode is out of scope. | Transient | + +## Informal specification + + +Operator **BatchNormalization** normalizes the input floating-point tensor $X$ along the channel axis using the pre-computed per-channel statistics $input\_mean$ and $input\_var$, then applies a per-channel affine transformation using $scale$ and bias $B$, and stores the result in output tensor $Y$. Computations are performed according to IEEE 754 floating-point semantics. + +The tensor $X$ has shape $(N, C, d_2, \ldots, d_{rX-1})$ where $N = dX_0$ is the batch size and $C = dX_1$ is the number of channels. When $X$ is a 1-D tensor of size $N$, the number of channels $C$ is implicitly assumed to be 1. + +The parameters $scale$, $B$, $input\_mean$, and $input\_var$ are 1-D tensors of size $C$, each indexed by the channel index $c \in [0, C-1]$. + +For any element of the input tensor, the output is computed as follows: + +$$ +Y[n, c, d_2, \ldots] = scale[c] \cdot \frac{X[n, c, d_2, \ldots] - input\_mean[c]}{\sqrt{input\_var[c] + \varepsilon}} + B[c] +$$ + +where: +- $n \in [0, N-1]$ is the batch index +- $c \in [0, C-1]$ is the channel index +- $d_k \in [0, dX_k - 1]$ for $k \in [2, rX-1]$ are the spatial indices +- $\varepsilon$ is the value of attribute `epsilon` + +### Examples + +#### Example 1 + +```math +X = \begin{bmatrix} 1.0 & 2.0 \\ 3.0 & 4.0 \end{bmatrix} +\quad +input\_mean = \begin{bmatrix} 2.0 \\ 3.0 \end{bmatrix} +\quad +input\_var = \begin{bmatrix} 1.0 \\ 1.0 \end{bmatrix} +``` + +```math +scale = \begin{bmatrix} 1.0 \\ 2.0 \end{bmatrix} +\quad +B = \begin{bmatrix} 0.0 \\ 1.0 \end{bmatrix} +\quad +\varepsilon = 10^{-5} +``` + +```math +Y \approx \begin{bmatrix} -1.0 & 1.0 \\ -3.0 & 3.0 \end{bmatrix} +\quad\text{(with B added: } \begin{bmatrix} -1.0 & 1.0 \\ -1.0 & 3.0 \end{bmatrix}\text{)} +``` + +#### Example 2 + +```math +X = \begin{bmatrix} + 3.0 & 4.5 \\ + 16.0 & 1.0 \\ + 25.5 & 24.25 +\end{bmatrix} +\quad +input\_mean = \begin{bmatrix} 3.0 \\ 4.5 \end{bmatrix} +\quad +input\_var = \begin{bmatrix} 4.0 \\ 2.25 \end{bmatrix} +``` + +```math +scale = \begin{bmatrix} 1.0 \\ 1.0 \end{bmatrix} +\quad +B = \begin{bmatrix} 0.0 \\ 0.0 \end{bmatrix} +\quad +\varepsilon = 10^{-5} +``` + +```math +Y \approx \begin{bmatrix} + 0.0 & 0.0 \\ + 6.5 & -2.333 \\ + 11.25 & 13.167 +\end{bmatrix} +``` + +## Error conditions + +No error condition applies in inference mode. Division by zero is prevented by the `epsilon` attribute, which ensures the denominator $\sqrt{input\_var[c] + \varepsilon}$ is strictly positive for any non-negative variance. + +## Attributes + +### `epsilon`: float + +A small positive value added to the variance before taking the square root, to avoid division by zero. + +Default value: $10^{-5}$ + +#### Constraints + +- `[C1]` Positivity + - Statement: `epsilon` shall be strictly positive: $\varepsilon > 0$. +### `momentum`: float + +Factor used when computing running mean and running variance during training. This attribute is irrelevant in inference mode ([R1](#R1)) and shall be ignored. + +Default value: $0.9$ + +#### Constraints + +No constraint applies to `momentum` in inference mode. + +### `training_mode`: int + +Flag indicating whether the operator is used in training mode. Only inference mode is in scope. + +Default value: $0$ (inference) + +#### Constraints + +- `[C1]` Inference mode only + - Statement: `training_mode` shall be $0$. +## Inputs + +### $X$: floating-point tensor + +Input tensor to be normalized. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: Tensors $X$ and $Y$ shall have the same type (float16, float, or double). +- `[C3]` Rank + - Statement: $X$ shall have rank $rX \geq 1$. +### $scale$: floating-point tensor + +Per-channel scaling factor $\gamma$, applied after normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: $scale$ shall be a 1-D tensor of size $C = dX_1$ (or size 1 when $rX = 1$). +- `[C2]` Type consistency + - Statement: $scale$ and $B$ shall have the same type (float16, float, or double). +### $B$: floating-point tensor + +Per-channel bias $\beta$, added after scaling. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_f) on $scale$. +- `[C2]` Type consistency + - Statement: see constraint [**[C2]**](#C2sc_f) on $scale$. +### $input\_mean$: floating-point tensor + +Per-channel estimated mean used for normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_f) on $scale$. +- `[C2]` Type consistency + - Statement: $input\_mean$ and $input\_var$ shall have the same type (float16, float, or double). +### $input\_var$: floating-point tensor + +Per-channel estimated variance used for normalization. + +#### Constraints + +- `[C1]` Shape + - Statement: see constraint [**[C1]**](#C1sc_f) on $scale$. +- `[C2]` Type consistency + - Statement: see constraint [**[C2]**](#C2mean_f) on $input\_mean$. +- `[C3]` Non-negativity + - Statement: $\forall c,\ input\_var[c] \geq 0$. +## Outputs + +### $Y$: floating-point tensor + +Output tensor, result of the normalization and affine transformation applied to $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: see constraint [**[C1]**](#C1X_f) on $X$. +- `[C2]` Type consistency + - Statement: see constraint [**[C2]**](#C2X_f) on $X$. +## Numeric accuracy + +[See the numeric accuracy note](./batchnorm_acc.md). + +## Formal specification + +See the Why3 specification. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/clip/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/clip/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/clip/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/clip/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/clip/clip.md b/safety-related-profile/sonnx/ops/spec/informal/clip/clip.md new file mode 100644 index 00000000..21712d35 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/clip/clip.md @@ -0,0 +1,310 @@ +# Contents + +- **Clip** operator for type [real](#real) +- **Clip** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) +- **Clip** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Clip version 13](https://onnx.ai/onnx/operators/onnx__Clip.html). + + +# **Clip** (real, real, real) + +## Signature +$Y = \textbf{Clip}(X,L,M)$ + +where: +- $X$: Input tensor +- $L$: Minimum value (scalar) +- $M$: Maximum value (scalar) +- $Y$: Output tensor + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +## Informal specification + +[t1] Operator **Clip** limit the given input within an interval. + +[t1.1] +- If $L$ $\leq$ $M$: + + - $Y[i]$ = $L$ if $X[i]$ $\lt$ $L$ + + - $Y[i]$ = $M$ if $X[i]$ $\gt$ $M$ + + - $Y[i]$ = $X[i]$, otherwise. + +[/t1.1] + +- If $L$ $\gt$ $M$: + + - $Y[i] = M$ + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +[/t1] + +Note: **Clip** can also be specified as : +$$ Y[i] = \min(M, \max(X[i], L))$$ + + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + + +### Example 1 + +```math +X = \begin{bmatrix} -6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +L = 0 +\quad +M = 10 +``` + + +```math +Y = \begin{bmatrix} 0 & 9.5 & 10 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +L = 20 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} 10 & 10 & 10 \end{bmatrix} +``` + +## Error conditions + +If any precondition is not satisfied, the behaviour of the operator is not defined. + +## Attributes + +Operator **Clip** has no attribute. + +## Inputs + +### $X$: `real` +Tensor $X$ is the input tensor to be clipped. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +### $L$: `real` +Tensor $L$ is a scalar tensor giving the minimum bound for clipping. + +### Constraints + - `[C1]` Shape consistency + - Statement: The shape of tensor $L$ must be empty. + - Rationale: $L$ is a scalar tensor + +### $M$: `real` +Tensor $M$ a scalar tensor giving the maximum bound for clipping. + +### Constraints + - `[C1]` Shape consistency + - Statement: The shape of tensor $M$ must be empty. + - Rationale: $M$ is a scalar tensor + +## Outputs + +### $Y$: `real` +Tensor $Y$ is the element-wise result of clipping $X$ by the interval $[L, M]$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + + +## Numerical Accuracy + +See the [Numerical accuracy specification](/clip_na.md). + + +# **Clip** (int, int, int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +See specification for [real numbers](#real). + + +# **Clip** (float, float, float) +where float is in {float16, float, double} + +## Signature +$Y = \textbf{Clip}(X,L,M)$ + +where: +- $X$: Input tensor +- $L$: Minimum value (scalar) +- $M$: Maximum value (scalar) +- $Y$: Output tensor + +## Restrictions +[General restrictions](../general_restrictions.md) are applicable. + +## Informal specification + +Operator **Clip** limit the given input within an interval. + +**Clip** operation is specified as follows: + + - If $L'$ $\leq$ $M'$: + + - if $X[i]$ $\lt$ $L'$ then $Y[i]$ = $L'$. + + - If $X[i]$ $\gt$ $M'$ then $Y[i]$ = $M'$. + - Otherwise, $Y[i]$ = $X[i]$. + + - If $L'$ $\gt$ $M'$: + - $Y[i] = M'$ + +where +- $i$ is a [tensor index](../common/definitions.md#tensor_index). +- $L'$ = -inf if $L$ is NaN otherwise $L'$ = $L$ +- $M'$ = +inf if $M$ is NaN otherwise $M'$ = $M$ + +Note: **Clip** **does not** behave as +$$ Y[i] = \min(M', \max(X[i], L'))$$ +when $L=+0$ and $M=-0$ + +For instance, $Y[i]=\min(-0,\max(-1.0, +0))=-0$ whereas the expected value from specification according to the IEEE754 rules for $+0$ and $-0$ comparisons is $+0$. + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + + +### Example 1 + +```math +X = \begin{bmatrix} -6.3 & 9.2 & 35.5 \end{bmatrix} +``` + +```math +L = 0.5 +\quad +M = 10.1 +``` + +```math +Y = \begin{bmatrix} 0.5 & 9.2 & 10.1 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} 6.5 & 9.2 & 35.1 \end{bmatrix} +``` + +```math +L = 20.2 +\quad +M = 10.0 +``` + +```math +Y = \begin{bmatrix} 10.0 & 10.0 & 10.0 \end{bmatrix} +``` + +### Example 3 + +```math +X = \begin{bmatrix} \text{-inf} & 0.0 & \text{+inf} & \text{NaN} \end{bmatrix} +``` +```math +L = -1.0 +\quad +M = \text{NaN} +``` + +```math +Y = \begin{bmatrix} -1.0 & 0.0 & \text{+inf} & \text{NaN} \end{bmatrix} +``` + +### Example 4 + +```math +X = \begin{bmatrix} \text{-inf} & 0.0 & \text{+inf} & \text{NaN} \end{bmatrix} +``` +```math +L = \text{NaN} +\quad +M = \text{NaN} +``` + +```math +Y = \begin{bmatrix} \text{-inf} & 0.0 & \text{+inf} & \text{NaN} \end{bmatrix} +``` + +## Error conditions +If any pre-conditions is not satisfied, then the behavior is undefined. + +## Attributes +Operator **Clip** has no attribute. + +## Inputs + +### $X$: `float` +Tensor $X$ is the input tensor to be clipped within the specified bounds. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + - `[C2]` Type consistency + - Statement: Tensors $X$, $L$, $M$ and $Y$ must have the same type. + +### $L$: `float` +Tensor $L$ is the minimum bound for clipping. + +### Constraints + +- `[C1]` Shape consistency + - Statement: The shape of tensor $L$ must be empty. + - Rationale: $L$ is a scalar tensor. + +- `[C2]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $X$. + + +### $M$: `float` +Tensor $M$ is the maximum bound for clipping. + +### Constraints + +- `[C1]` Shape consistency + - Statement: The shape of tensor $max$ must be empty. + - Rationale: $M$ is a scalar tensor. + +- `[C2]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $input$. +## Outputs + +### $output$: [float](#float) +Tensor $output$ is the element-wise result of clipping $input$ by the interval $[min, max]$. +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $input$. + - `[C2]` Type consistency + - Statement: see constraint [ [C2]](#C2ia) on tensor $input$. + +## Formal specification + +See the [Why3 specification](../formal/clip). + +## Numerical Accuracy + +See the [Numerical accuracy specification](/clip_na.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/jean-eric.md b/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/jean-eric.md new file mode 100644 index 00000000..6ad95735 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/jean-eric.md @@ -0,0 +1,476 @@ +# Contents + +- **clip** operator for type [real](#real) +- **clip** operator for types [INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64](#int) +- **clip** operator for types [FP16, FP32, FP64, BFLOAT16](#floats) + +Based on ONNX documentation version 13. + + +# **clip** (real) + +## Signature +$Y = \text{clip}(X,L,M)$ + +where: +- `X`: input tensor +- `L`: min value (scalar tensor) +- `M`: max value (scalar tensor) + +## Restrictions +The following restrictions apply to the **clip** operator for the SONNX profile: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input L must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | Input M must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Sparse tensors are not supported | General restriction [GR1](../general_restrictions.md#GR1) | +| `[R4]` | Shape of tensors shall be explicit | General restriction [GR2](../general_restrictions.md#GR2) | + +## Informal specification + +Operator **clip** limits the input within an interval. For each element in the input tensor `X`: +- If `L` $\leq$ `M`: + - if its value is less than `L` then it is replaced by `L`. + - If its value is greater than `M` then it is replaced by `M`. + - Otherwise, the value is unchanged. +- If `L` $\gt$ `M`: + - all values are set to `M`. + +> Rather than "its", please us $X[i]$ with $i$ the index (see the DIV operator). + +The result is stored in output tensor `Y`. + +If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $Y[i]$ is the result of clipping $X[i]$ by the interval $[L, M]$. + +> To not define the "clip" operator using "clipping". Instead, simply ref to the following formula. + +For any index $i$, +$$ +Y[i] = \min(M, \max(X[i], L)) +$$ + +> To be checked. It does not display correctly on github... + +Wich is equivalent to: + +$$ +Y[i] = +\begin{cases} +M & \text{if } L \gt M \\ +\\ +\begin{cases} +L & \text{if } X[i] \leq L \\ +M & \text{if } X[i] \geq M \\ +X[i] & \text{otherwise} +\end{cases}& otherwise +\end{cases} +$$ + +> This last representation is not necessary (min(max...) is sufficient.) + +### Example 1 + +```math +A = \begin{bmatrix} -6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +L = 0 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} 0 & 9.5 & 10 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +L = 20 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(6.1, 20)) & \min(10, \max(9.5, 20)) & \min(10, \max(35.7, 20)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10 & 10 & 10 \end{bmatrix} +``` + +## Error conditions +No error condition + +## Inputs + +### $X$: real +Tensor `X` is the input tensor to be clipped within the specified bounds. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors `X` and `Y` must have the same shape. + +### $L$: real +Tensor `L` is the minimum bound for clipping. + +Tensor `L` must be a scalar (empty shape). + +### Constraints +Tensor `L` has no constraints. + +### $M$: real +Tensor `M` is the maximum bound for clipping. + +Tensor `M` must be a scalar (empty shape). + +### Constraints +Tensor `M` has no constraints. + +## Outputs + +### $Y$: real +Tensor `Y` is the element-wise result of clipping `X`. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + - `[C2]` Interval consistency + - Statement: Output tensor entries should either lie in the interval $[L, M]$ or all be equal to `M`. + +> C2 is not a constraint (a pre-condition for the appropriate usage of the operator), its a post condition. To be suppressed. + +## Attributes + +Operator **clip** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **clip** does not introduce any numerical error. + + +# **clip** (int, int, int) +where int is in {INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64} + +## Signature +$Y = \text{clip}(X,L,M)$ + +where: +- `X`: input tensor +- `L`: minimum tensor (empty shape tensor - scalar) +- `M`: maximum tensor (empty shape tensor - scalar) + +> **Please report all modifications done on the "real" type to the other data types...** + +## Restrictions +The following restrictions apply to the **clip** operator for the SONNX profile: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input L must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | Input M must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Sparse tensors are not supported | General restriction [GR1](../general_restrictions.md#GR1) | +| `[R4]` | Shape of tensors shall be explicit | General restriction [GR2](../general_restrictions.md#GR2) | +| `[R5]` | All tensors shall have the same datatype | General restriction [GR3](../general_restrictions.md#GR3) | +## Informal specification + +Operator **clip** limit the given input within an interval. For each element in the input tensor `X`: +- If `L` $\leq$ `M`: + - if its value is less than `L` then it is replaced by `L`. + - If its value is greater than `M` then it is replaced by `M`. + - Otherwise, the value is unchanged. +- If `L` $\gt$ `M`: + - all values are set to `M`. + +The result is stored in output tensor `Y`. + +If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $Y[i]$ is the result of clipping $X[i]$ by the interval $[L, M]$. + +For any index $i$, +$$ +Y[i] = \min(M, \max(X[i], L)) +$$ + +Wich is equivalent to: + +$$ +Y[i] = +\begin{cases} +M & \text{if } L \gt M \\ +\\ +\begin{cases} +L & \text{if } X[i] \leq L \\ +M & \text{if } X[i] \geq M \\ +X[i] & \text{otherwise} +\end{cases}& otherwise +\end{cases} +$$ + +### Example 1 + +```math +A = \begin{bmatrix} -6 & 9 & 35 \end{bmatrix} +``` + +```math +L = 0 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} 0 & 9 & 10 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6 & 9 & 35 \end{bmatrix} +``` + +```math +L = 20 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(6, 20)) & \min(10, \max(9, 20)) & \min(10, \max(35, 20)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10 & 10 & 10 \end{bmatrix} +``` + +## Error conditions +No error condition + +## Inputs + +### $X$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor `X` is the input tensor to be clipped within the specified bounds. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors `X` and `Y` must have the same shape. + - `[C2]` Type consistency + - Statement: Tensors `X`, `L`, `M` and `Y` must have the same type. + +### $L$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor `L` is the minimum bound for clipping. + +The shape of tensor `L` must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor `X`. + +### $M$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor `M` is the maximum bound for clipping. + +The shape of tensor `M` must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor `X`. + +## Outputs + +### $Y$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor `Y` is the element-wise result of clipping `X` by the interval $[L, M]$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + - `[C2]` Interval consistency + - Statement: Output tensor entries should either lie in the interval $[L, M]$ or all be equal to `M`. + + + - `[C3]` Type consistency + - Statement: see constraint [ [C2]](#C2ia) on tensor `X`. + +## Attributes + +Operator **clip** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **clip** does not introduce any numerical error. Hence, for all valid indexes $i$, + + + +# **clip** (float, float, float) +where float is in {FP16, FP32, FP64, BFLOAT16} + +## Signature +$Y = \text{clip}(X,L,M)$ + +where: +- `X`: input tensor +- `L`: minimum tensor (empty shape tensor - scalar) +- `M`: maximum tensor (empty shape tensor - scalar) + +## Restrictions +The following restrictions apply to the **clip** operator for the SONNX profile: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input L must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | Input M must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Sparse tensors are not supported | General restriction [GR1](../general_restrictions.md#GR1) | +| `[R4]` | Shape of tensors shall be explicit | General restriction [GR2](../general_restrictions.md#GR2) | +| `[R5]` | All tensors shall have the same datatype | General restriction [GR3](../general_restrictions.md#GR3) | +## Informal specification + +Operator **clip** limit the given input within an interval. For each element in the input tensor `X`: +- If `L` $\leq$ `M`: + - if its value is less than `L` then it is replaced by `L`. + - If its value is greater than `M` then it is replaced by `M`. + - Otherwise, the value is unchanged. +- If `L` $\gt$ `M`: + - all values are set to `M`. + +The result is stored in output tensor `Y`. + +If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $Y[i]$ is the result of clipping $X[i]$ by the interval $[L, M]$. + +For any index $i$, +$$ +Y[i] = \min(M, \max(X[i], L)) +$$ + +Wich is equivalent to: + +$$ +Y[i] = +\begin{cases} +M & \text{if } L \gt M \\ +\\ +\begin{cases} +L & \text{if } X[i] \leq L \\ +M & \text{if } X[i] \geq M \\ +X[i] & \text{otherwise} +\end{cases}& otherwise +\end{cases} +$$ + +### Example 1 + +```math +A = \begin{bmatrix} -6.3 & 9.2 & 35.5 \end{bmatrix} +``` + +```math +L = 0.5 +\quad +M = 10.1 +``` + +```math +Y = \begin{bmatrix} 0.5 & 9.2 & 10.1 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6.5 & 9.2 & 35.1 \end{bmatrix} +``` + +```math +L = 20.2 +\quad +M = 10.0 +``` + +```math +Y = \begin{bmatrix} \min(10.0, \max(6.5, 20.2)) & \min(10.0, \max(9.2, 20.2)) & \min(10.0, \max(35.1, 20.2)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10.0 & 10.0 & 10.0 \end{bmatrix} +``` + +## Error conditions + +> The following may be discussed since the oeprator does not generate per se special values. It simply propagate them. So it is not a an error condition... +- Values of the output tensor may be IEEE 754 infinity or NaN. + +> Move the following in the definition of the operator. (cf DIV). It could be useful to the user to explain what happens with NaN and Inf... or give appropriate examples. + +- Comparisons with NaN follow IEEE 754 semantics + +## Inputs + +### $X$: FP16, FP32, FP64, BFLOAT16 +Tensor `X` is the input tensor to be clipped within the specified bounds. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors `X` and `Y` must have the same shape. + - `[C2]` Type consistency + - Statement: Tensors `X`, `L`, `M` and `Y` must have the same type. + + +### $L$: FP16, FP32, FP64, BFLOAT16 +Tensor `L` is the minimum bound for clipping. + +The shape of tensor `L` must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor `X`. + +### $M$: FP16, FP32, FP64, BFLOAT16 +Tensor `M` is the maximum bound for clipping. + +The shape of tensor `M` must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor `X`. + +## Outputs + +### $Y$: FP16, FP32, FP64, BFLOAT16 +Tensor `Y` is the element-wise result of clipping `X` by the interval $[L, M]$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + - `[C2]` Interval consistency + - Statement: Output tensor entries should either lie in the interval $[L, M]$ or all be equal to `M`. + + - `[C3]` Type consistency + - Statement: see constraint [ [C2]](#C2ia) on tensor `X`. + +## Attributes + +Operator **clip** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **clip** does not introduce any numerical error. Hence, for all valid indexes $i$, \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/wgtls_2026_01_15.md b/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/wgtls_2026_01_15.md new file mode 100644 index 00000000..f84c175c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/clip/reviews/wgtls_2026_01_15.md @@ -0,0 +1,506 @@ +# Contents + +- **Clip** operator for type [real](#real) +- **Clip** operator for types [INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64](#int) +- **Clip** operator for types [FP16, FP32, FP64, BFLOAT16](#floats) + +> Use lowercases for all types, throughout the document. +> For FP numbers, use the ONNX standard types: float16, float, double... +> Remove BFLOAT16 (not supported by IEEE) + +Based on ONNX documentation version 13. + + +# **Clip** (real) + +## Signature +$Y = \text{Clip}(X,L,M)$ + +where: +- $X$: input tensor +- $L$: minimum tensor (empty shape tensor - scalar) +- $M$: maximum tensor (empty shape tensor - scalar) + +> Use the same names as ONNX, i.e., "input", "min", "max", "output". +> remove "empty shape tensor" and simply use "scalar". +> In order to keep mathematical formulae simple, introduce a simpler denotation (if needed). +> For instance: +>- $input$: input tensor (denoted by $X$) +>- $min$: minimum value (scalar) (denoted by $L$) +>- $max$: maximum value (scalar) (denoted by $M$) + +## Restrictions +The following restrictions apply to the $\text{Clip}$ operator for the SONNX profile: + +[General restrictions](../general_restrictions.md) are applicable. + +## Informal specification + +> As stated before, at interface level we must use ONX names ("min", "max",...). But for the formulae, we should probably stick to mathematical symbols. + +Operator $\text{Clip}$ limit the given input within an interval. For each element in the input tensor $X$: +- If $L$ $\leq$ $M$: + - if $X[i] < L$ then $Y[i]$ = $L$. + - If $X[i] > M$ then $Y[i]$ = $M$. + - Otherwise, $Y[i] = X[i]$. +- If $L$ $\gt$ $M$: + - $Y[i] = M$ + +> /!\ See the new formatting (pb with ">" and "<"). Do as shown above. +> Remove "forall i" (because "i" is used before) + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The result is stored in output tensor $Y$. + +> The use of "stored" is strange since (conceptually) a tensor is a mathematical object (not a variable). +> And the specification defines $Y$, so this sentence could well be removed (everywhere). + +Clip operation can ALSO be expressed as: + +$$\forall i,\ Y[i] = \min(M, \max(X[i], L))$$ + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +### Example 1 + +```math +A = \begin{bmatrix} -6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +> "A" is "X" + +```math +L = 0 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(-6.1, 0)) & \min(10, \max(9.5, 0)) & \min(10, \max(35.7, 0)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 0 & 9.5 & 10 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +L = 20 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(6.1, 20)) & \min(10, \max(9.5, 20)) & \min(10, \max(35.7, 20)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10 & 10 & 10 \end{bmatrix} +``` + +## Error conditions +No error condition + +## Inputs + +### $\text{X}$: `real tensor` +Tensor $X$ is the input tensor to be clipped within the specified bounds. + +> As constraints refer to the ONNX interface definition, don't forget to rename the tensors (L=>"min", M=>"Max", etc.) + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +### $\text{L}$: `real tensor` +Tensor $L$ is the minimum bound for clipping. + +The shape of tensor $L$ must be empty (scalar). +> Remove this as it is a constraint. + +### Constraints +Tensor $L$ has no constraints. + +> Add a constraint about the shape (same for "M"): the shape of tensor "min" must be empty. + +### $\text{M}$: `real tensor` +Tensor $M$ is the maximum bound for clipping. + +The shape of tensor $M$ must be empty (scalar). + +### Constraints +Tensor $M$ has no constraints. + +## Outputs + +### $\text{Y}$: `real tensor` +Tensor $Y$ is the element-wise result of clipping $X$ by the interval $[L, M]$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Attributes + +Operator $\text{Clip}$ has no attribute. + +## Formal specification + +See the [Why3 specification](). +> Add a hyperlink to the formal spec. + +## Numerical Accuracy + +Operator $\text{Clip}$ does not introduce any numerical error. Hence, for all valid indexes, the output values are exactly equal to the corresponding input values. + +> Replace by a link to file "clip_na.md" (located at the same level as the informal spec.). This file will contain part of the spec concerning numerical accuracy. + + +# **clip** (int, int, int) +where int is in {INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64} + +> First letter of the operator must be capitalized. +> All types in lowercases +> Add a remark about the fact that the values used in all arguments are of the same type (even though it is implicit). + +## Signature +$Y = \text{Clip}(X,L,M)$ + +where: +- $X$: input tensor +- $L$: minimum tensor (empty shape tensor - scalar) +- $M$: maximum tensor (empty shape tensor - scalar) +## Restrictions +The following restrictions apply to the $\text{Clip}$ operator for the SONNX profile: + +[General restrictions](../general_restrictions.md) are applicable. +## Informal specification + +Operator $\text{Clip}$ limit the given input within an interval. For each element in the input tensor $X$: +- If $L$ $\leq$ $M$: + - if $X[i]$ < $L$ then $Y[i]$ = $L$. + - If $X[i]$ > $M$ then $Y[i]$ = $M$. + - Otherwise, $Y[i]$ = $X[i]$. +- If $L$ $\gt$ $M$: + - $\forall i,\ Y[i] = M$ + +> Same as above: remove "forall i". + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The result is stored in output tensor $Y$. + +Clip operation can be expressed as: + +$$\forall i,\ Y[i] = \min(M, \max(X[i], L))$$ + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +### Example 1 + +```math +A = \begin{bmatrix} -6 & 9 & 35 \end{bmatrix} +``` + +```math +L = 0 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(-6, 0)) & \min(10, \max(9, 0)) & \min(10, \max(35, 0)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 0 & 9 & 10 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6 & 9 & 35 \end{bmatrix} +``` + +```math +L = 20 +\quad +M = 10 +``` + +```math +Y = \begin{bmatrix} \min(10, \max(6, 20)) & \min(10, \max(9, 20)) & \min(10, \max(35, 20)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10 & 10 & 10 \end{bmatrix} +``` + +## Error conditions +No error condition + +## Inputs + +### $\text{X}$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor $X$ is the input tensor to be clipped within the specified bounds. + +> replace the list of types by "int", where "int" refers to the interface definition. That gives: +> **input: int** + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + - `[C2]` Type consistency + - Statement: Tensors $X$, $L$, $M$ and $Y$ must have the same type. + +### $\text{L}$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor $L$ is the minimum bound for clipping. + +The shape of tensor $L$ must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $X$. + +### $\text{M}$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor $M$ is the maximum bound for clipping. + +The shape of tensor $M$ must be empty (scalar). +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $X$. + +## Outputs + +### $\text{Y}$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64 +Tensor $Y$ is the element-wise result of clipping $X$ by the interval $[L, M]$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + - `[C2]` Type consistency + - Statement: see constraint [ [C2]](#C2ia) on tensor $X$. + +## Attributes + +Operator $\text{Clip}$ has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator $\text{Clip}$ does not introduce any numerical error. Hence, for all valid indexes, the output values are exactly equal to the corresponding input values. + + + +# **Clip** (float, float, float) +where float is in {FP16, FP32, FP64, BFLOAT16} + +> Replace FP16 by "float16", FP32 by float,... +> Remove BFLOAT16 (which is not IEEE754) + +## Signature +$Y = \text{Clip}(X,L,M)$ +where: +- $X$: input tensor +- $L$: minimum tensor (empty shape tensor - scalar) +- $M$: maximum tensor (empty shape tensor - scalar) + +## Restrictions +The following restrictions apply to the $\text{Clip}$ operator for the SONNX profile: + +[General restrictions](../general_restrictions.md) are applicable. + +## Informal specification + +Operator $\text{Clip}$ limits the given input within an interval. For each element in the input tensor $X$ (discarding $nan$ values in $L$ or $M$, for more details see the definition below): +> Remove "For each...". + +- If $L$ $\leq$ $M$: + + - if $X[i]$ < $L$ then $Y[i]$ = $L$. + + - If $X[i]$ > $M$ then $Y[i]$ = $M$. + - Otherwise, $Y[i]$ = $X[i]$. +- If $L$ $\gt$ $M$: + - $\forall i,\ Y[i] = M$ + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The result is stored in output tensor $Y$. + +Clip operation can be divided into two steps: + +- If any of the boundaries is $nan$ it is readjusted to the respetive extreme value: + + - If $L$ is $nan$, it is set to the lowest representable value. + +> "nan" => "NaN" +> +> [IMPORTANT] Considering your example (onnxruntime), L must be substituted by -inf + + - If $M$ is $nan$, it is set to the highest representable value. + +> [IMPORTANT] Considering your example (onnxruntime), M must be substituted by +inf + +- The clipping is then performed as: + +> Move the previous items about NaNs in the spec "if L <= M ...", with a specific test. + +$$\forall i,\ Y[i] = \min(M, \max(X[i], L))$$ + +where $i$ is a [tensor index](../common/definitions.md#tensor_index). + + +### Example 1 + +```math +A = \begin{bmatrix} -6.3 & 9.2 & 35.5 \end{bmatrix} +``` + +```math +L = 0.5 +\quad +M = 10.1 +``` +```math +Y = \begin{bmatrix} \min(10.1, \max(-6.3, 0.5)) & \min(10.1, \max(9.2, 0.5)) & \min(10.1, \max(35.5, 0.5)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 0.5 & 9.2 & 10.1 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 6.5 & 9.2 & 35.1 \end{bmatrix} +``` + +```math +L = 20.2 +\quad +M = 10.0 +``` + +```math +Y = \begin{bmatrix} \min(10.0, \max(6.5, 20.2)) & \min(10.0, \max(9.2, 20.2)) & \min(10.0, \max(35.1, 20.2)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 10.0 & 10.0 & 10.0 \end{bmatrix} +``` + +### Example 3 + +```math +A = \begin{bmatrix} -\infty & 0.0 & \infty & NaN \end{bmatrix} +``` +```math +L = -1.0 +\quad +M = NaN +``` +```math +M = h +``` +where `h` is the highest representable value. + +```math +Y = \begin{bmatrix} \min(h, \max(-\infty, -1.0)) & \min(h, \max(0.0, -1.0)) & \min(h, \max(\infty, -1.0)) & \min(h, \max(NaN, -1.0)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} -1.0 & 0.0 & \infty & NaN \end{bmatrix} +``` + +### Example 4 + +```math +A = \begin{bmatrix} -\infty & 0.0 & \infty & NaN \end{bmatrix} +``` +```math +L = NaN +\quad +M = NaN +``` +```math +L = l \quad M = h +``` +where: + - `l` is the lowest representable value and + - `h` is the highest representable value. + +```math +Y = \begin{bmatrix} \min(h, \max(-\infty, l)) & \min(h, \max(0.0, l)) & \min(h, \max(\infty, l)) & \min(h, \max(NaN, l)) \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} -\infty & 0.0 & \infty & NaN \end{bmatrix} +``` + +## Error conditions +- Values of the output tensor may be IEEE 754 infinity or NaN + +## Inputs + +### $\text{X}$: FP16, FP32, FP64, BFLOAT16 +Tensor $X$ is the input tensor to be clipped within the specified bounds. + +### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + - `[C2]` Type consistency + - Statement: Tensors $X$, $L$, $M$ and $Y$ must have the same type. + +### $\text{L}$: FP16, FP32, FP64, BFLOAT16 +Tensor $L$ is the minimum bound for clipping. + +The shape of tensor $L$ must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $X$. + +### $\text{M}$: FP16, FP32, FP64, BFLOAT16 +Tensor $M$ is the maximum bound for clipping. + +The shape of tensor $M$ must be empty (scalar). + +### Constraints +- `[C1]` Type consistency + - Statement: see constraint [[C2]](#C2ia) on tensor $X$. +## Outputs + +### $\text{Y}$: FP16, FP32, FP64, BFLOAT16 +Tensor $Y$ is the element-wise result of clipping $X$ by the interval $[L, M]$. +### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + - `[C2]` Type consistency + - Statement: see constraint [ [C2]](#C2ia) on tensor $X$. + +## Attributes + +Operator $\text{Clip}$ has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy +Operator $\text{Clip}$ does not introduce any numerical error. Hence, for all valid indexes, the output values are exactly equal to the corresponding input values. diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/README.md b/safety-related-profile/sonnx/ops/spec/informal/common/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_pad2.png b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_pad2.png new file mode 100644 index 00000000..84ede13c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_pad2.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_stride3.png b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_stride3.png new file mode 100644 index 00000000..39928a23 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/conv_stride3.png differ diff --git a/safety-related-profile/documents/conv_specification_example/imgs/dilation.png b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/dilation.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/dilation.png rename to safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/dilation.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/onnx_conv_padop2.png b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/onnx_conv_padop2.png new file mode 100644 index 00000000..5109e8df Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/assets/sliding_window_ops/imgs/onnx_conv_padop2.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/Answers to Edoardo's review.md b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/Answers to Edoardo's review.md new file mode 100644 index 00000000..7e822a4d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/Answers to Edoardo's review.md @@ -0,0 +1,35 @@ +**Note 1: all questions and comments are explicitly numbered. Whenever I have suggestions in terms of writing/phrasing, I just changed the text (highlighted in bold capital font).** + +*Got it. The document was modified condidering all suggestions highlighted in bold capital font except one. The numbered questions and comments and the suggestion not taken into acount are discussed below for future decision.* + +**Note 2: it is unclear why L cannot be 2^31-1. Why "minus one"?** + +*The broadcast was defined initialy in the scope of the Max operator. In the definition of the Max operator (https://onnx.ai/onnx/operators/onnx__Max.html) it is indicated "Between 1 and 2147483647 inputs". 2147483648 = 2^31. Thus the number of inputs for the Max operator is 2^31-1. Moreover, note that L is the last index. I.e. the input tensors are numbered from 0 to L. In consequence the L is the number of inputs minus one. Note that this constraint is only relevant for the operators Max, Mean, Min and Sum. Other operators where broadcasting is applicable have only two or three inputs.* + +**Note 3: this remark (the access to the tensor data remains possible) is unclear. Is it a statement about copying the data vs accessing by reference? Or is it a subtle reference to the Description fo Step 1 below? Is the meaning something like "during the broadcasting, the data of the input tensor remains in place", where "in place" is to be intended as the technical memory access terminology? In general, it seems the intention here is to say something about the implementation of the broadcasting operator, which I am not sure is the desired goal.** + +*The intention is not to say something about the implementation. The intention of the three bullet points is to indicate that there are consequences on (1) the number of dimensions, (2) the size of each dimension and (3) the indexes used to access the data. May be we could change this last bullet point by "access to the data through tensor index is modified accordingly"* + +**"IF THAT IS THE CASE, THE MISSING DATA IS FILLED BY COPYING THE ENTRIES AT INDEX 0 OF THE CORRESPONDING DIMENSION" instead of "If that is the case, the data associated with indexes larger than the input tensor dimension size is the data associated to index value 0 in the input tensor".** + +*"COPYING" may indicate a specific implementation.* + +**Note 4: the notation was difficult to parse, I have tried to improve it.** + +*Fine. I think it improves the notation.* + +**Note 5: the link below ("Origin") does not cover the error conditions explicitly. Examples of errors are in the numpy documentation linked therein. Are we happy with the reader having to follow two links to get to the actual information? https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules** + +*We could provide both links to the reader* + +**Note 6: for m ∈ [ 0 , L ] means that there are L + 1 input tensors. This issue is present throughout the document.** + +*We made the choice to number the inputs in the same way than the indexes, i.e. from 0 to N-1. We use L for last index instead of N for number of elements.* + +**Note 7: the shape might change, so is it really the same "type"? Maybe we can say "the entries of Xm and Zm must have the same numerical type"** + +*Moreover, it depends on the the operator using broadcasting... For instance the Where operator makes broadcasting from one bool tensor and two other data types tensors. I think that "numerical type" is the little restrictive because it is also applicable for string. May be "the entries of the tensors Xm and Zm must have the same data type."* + +**Note 8: see note 6** + +*See answer to notes 6 and 2.* diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Broadcast.drawio b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Broadcast.drawio new file mode 100644 index 00000000..d6465537 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Broadcast.drawio @@ -0,0 +1,661 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Y.drawio b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Y.drawio new file mode 100644 index 00000000..c5fda972 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/Y.drawio @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/f.drawio b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/f.drawio new file mode 100644 index 00000000..6f913965 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/drawio/f.drawio @@ -0,0 +1,310 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Broadcast.png b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Broadcast.png new file mode 100644 index 00000000..e5121131 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Broadcast.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Y.png b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Y.png new file mode 100644 index 00000000..ca7f8a81 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/Y.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/f.png b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/f.png new file mode 100644 index 00000000..9b514c12 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/assets/imgs/f.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast.md b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast.md new file mode 100644 index 00000000..8eb762b0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast.md @@ -0,0 +1,170 @@ +# Contents +- **Broadcast** functionality for type [real, float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, string, boolean](#any). + +Based on ONNX documentation version 14, multidirectional broadcasting. + +# **Broadcast** (type, type, type...) + +where type is in {real, float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, string, boolean}. + +## Signature +Definition of functionality $\text{Broadcast}$ signature: $Z0, ..., ZL = \text{Broadcast}(X0, ... , XL)$ + +where +- $L \in [0, 2^{31}-1[$: number of tensors to be broadcasted minus one. Note that in ONNX broadcasting is applied to operators with 2 inputs, 3 inputs or a variable number of inputs between 1 and 2147483647 (cf. operators Max https://onnx.ai/onnx/operators/onnx__Max.html , Mean, Min and Sum). $2^{31}$ = 2147483648, thus $2^{31}-1$ = 2147483647 is the maximum number of inputs. Moreover, note that $L$ is the last index. I.e. the input tensors are numbered from 0 to $L$. In consequence $L$ is the number of inputs minus one and cannot be equal to $2^{31}-1$. +- $X0$, ... , $XL$: tensors to be broadcasted. +- $Z0$, ... , $ZL$: broadcasted tensors. + +## Link to ONNX description + +https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md + +## Restrictions + +No restriction. + +## Informal specification + +The broadcasting functionality allows element-wise operations, e.g. **Add** , **Mul** , etc., to take tensors with different shapes by explicitly normalising them to the same shape. + +Broadcasting consists in producing a set of tensors with the same shape: I.e. each output tensor will have a number of dimensions $nZ$ and for each dimension $i$, a size $dZ_i$. To achieve this goal, each produced tensor $Zi$ contains elements from $Xi$ repeated as necessary. + +The shape of a $Zi$ satisfies two conditions. + +*Condition 1*: the number of dimensions of all output tensors is the largest number of dimensions among all the input tensors. When the number of dimensions is increased for a tensor, i.e. it is expanded: +- the extra dimensions to be completed are prepended before the lower indexes, +- those dimensions are set to a size equal to 1, and +- the access to the data through tensor index is modified accordingly. + +*Condition 2*: the size of each output dimension is equal to the maximum of the sizes of all the input tensors for that dimension after expansion. As a consequence the size of some dimensions in an output tensor might be larger than that of the corresponding input tensor. If that is the case, the data associated with indexes larger than the input tensor dimension size corresponds to the data associated to index value 0 in the input tensor (depending on implementation it can be a copy or just an index manipulation). [Note that, after expansion, the size of the dimension shall be either the maximum size or one, cf. error condition.](#error) + +The figure below shows an example of broadcasting two tensors, i.e. $Z0, Z1 = \text{Broadcast}(X0, X1)$. + +drawing + +The operation can be described in two steps: +- Step 1: make tensors with the same number of dimensions to ensure condition 1 +- Step 2: make the tensor dimension sizes equal to ensure condition 2 + +Let us note $Y0$, ... $YL$ the tensors obtained after step 1, with a common number of dimensions $nY$ but with still different dimension sizes. + +### Description of step 1 + +The common number of dimensions is the largest number of dimensions among the input tensors: + +$$nY = \max_{m \in [0, L]} nXm$$ + +where $nXm$ is the number of dimensions of $Xm$ the $m^{\text{th}}$ input tensor. + +The tensors with $nY > nXm$ have their dimensions prepended with $nY - nXm$ times 1. That is: + +$$\forall m \in [0,L] \forall i \in [0,nY-1] ~~~~~ dYm_i = 1 ~~~~~ \text{if} ~~~~~ i < nY - nXm, ~~~~~ dXm_{i-nY+nXm} ~~~~~ \text{otherwise}$$ + +Concerning the access to data through [tensor index](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md#tensor_index) we have the following relation: + +$$Ym[i]=Xm[i']$$ + +with $i$ defined from $i'$: + +$$i= \underbrace{0,...,0},i' + \atop nY-nXm$$ +### Description of step 2 + +Now $Y0$, ... $YL$ have a common number of dimensions but different dimension sizes. This common number of dimensions, is the number of dimensions of the output tensors: + +$$ nZm = nY $$ + +In order to define the output we have to set each dimension to its maximum among all tensors and, for tensors with increased dimension, access always to the first element whatever the value of the index in this dimension. + +For our running example, the figure below presents for the example of the figure above, the tensors obtained at the end of step 1. + +drawing + +#### Setting each dimension to its maximum + +The size of dimensions $dY_0$, ... $dY_{nY-1}$ are defined as the maximum over all tensors, i.e. $dY_i = \max_{m \in [0, L] } dYm_i$ for all $i\in[0,nY-1]$ where $dYm_0$, ... $dYm_{nY-1}$ are the dimensions of tensor $Ym$. Overall, we have: + +$$\forall m \in [0,L] \forall i \in [0,nZm-1] ~~~~~ dZm_i = dY_i$$ + +#### Access to data + +Let $f(.,.,.)$ a function that provides an index value of 0 when the dimension size is not the maximum size and the current index of this dimension when the dimension size is equal to the maximum dimension size. This function is defined as: + +$f(a,B,C) = a$ if $B=C$ and $f(a,B,C) = 0$ otherwise where: +- $a$ is the current index, +- $B$ is the size of the input dimension, and +- $C$ is the target size of the output dimension. + +When, for a given dimension $k$, of size $dYm_k$, of a tensor $Ym$, $dYm_k \neq dY_k$ then $f(i_k,dYm_k,dY_k)$ is 0 whatever the value of the index $i_k$. This way, $Zm$ relates always to the first element of $Ym$ in the $k^{\text{th}}$ dimension. + +Then the relation between elements of broadcasted tensors $Z0,...ZL$ and tensors with common number of dimensions $Y0,...YL$ are: + +$$Zm[i]=Ym[i']$$ + +with: + +$$i=i_0,...i_{nY-1}$$ + +$$i'=f(i_0,dYm_0,dY_0),...f(i_{nY-1},dYm_{nY-1},dY_{nY-1})$$ + +For our running example, the figure below presents for the example of the figure above, the way the function $f(.,.,.)$ allows to link elements of $Z0$ and $Z1$ with respectively elements of $Y0$ and $Y1$. + +drawing + +## Error conditions + +The following error condition applies to broadcasting: + +| Error | Statement | Origin | +| -------- | ------- | ------- | +| `E1` | $\exists m \in [0, L], \exists i \in [0, nY-1]$ such that $dYm_i \neq dY_i$ and $dYm_i \neq 1$| The ONNX documentation https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md refers to Numpy rules https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules| + +For each tensor and each dimension, considering a common number of dimensions, the size of a dimension shall be either equal to the maximum dimension size among all tensors or equal to one. If not, Error `E1` occurs. + +## Inputs + +### $Xm$ for $m \in [0,L]$: `type tensor` +$L-1$ input tensors with possibly different shapes. + +Remember that this specification use $L$ for last index instead of $N$ for number of elements, [cf. Signature](#last). The inputs are numbered in the same way than the indexes, i.e. from 0 to $N$-1, where $N$ is the number of inputs. + +#### Constraints + + - `[C1]` Type consistency + - Statement: For any $m \in [0,L]$ tensors $Xm$ and $Zm$ must have the same type. + +## Outputs + +### $Zm$ for $m \in [0,L]$: `type tensor` + +$L-1$ output tensors with identical shapes. + +Remember that this specification use $L$ for last index instead of $N$ for number of elements, [cf. Signature](#last). The outputs are numbered in the same way than the indexes, i.e. from 0 to $N$-1, where $N$ is the number of outputs. + +#### Constraints + + - `[C1]` Type consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $Xm$. + +## Attributes + +No attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Not applicable. I.e. the data values of the output tensors are stricly equal to the corresponding data values of the input tensors. + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast_revision_edoardo_manino.md b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast_revision_edoardo_manino.md new file mode 100644 index 00000000..9e5fdaf6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/broadcast/broadcast_revision_edoardo_manino.md @@ -0,0 +1,171 @@ +**Note 1: all questions and comments are explicitly numbered. Whenever I have suggestions in terms of writing/phrasing, I just changed the text (highlighted in bold capital font).** + +# Contents +- **Broadcast** functionality for type [real, float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, string, boolean](#any). + +Based on ONNX documentation version 14, multidirectional broadcasting. + +# **Broadcast** (type, type, type...) + +where type is in {real, float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, string, boolean}. + +## Signature +Definition of functionality $\text{Broadcast}$ signature: $Z0, ..., ZL = \text{Broadcast}(X0, ... , XL)$ + +where +- $L \in [0, 2^{31}-1[$: number of tensors to be broadcasted minus one **Note 2: it is unclear why L cannot be 2^31-1. Why "minus one"?** +- $X0$, ... , $XL$: tensors to be broadcasted +- $Z0$, ... , $ZL$: broadcasted tensors + +## Link to ONNX description + +https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md + +## Restrictions + +No restriction. + +## Informal specification + +The broadcasting functionality allows element-wise operations, e.g. **Add** , **Mul** , etc., to take tensors with different shapes, **BY EXPLICITLY NORMALISING THEM TO THE SAME SHAPE.** + +Broadcasting consists in producing a set of tensors with the same shape: **I.E. EACH OUTPUT TENSOR WILL HAVE** number of dimensions $nZ$ and for each dimension $i$, size $dZ_i$. TO ACHIEVE THIS GOAL,** each produced tensor $Zi$ contains elements from $Xi$ repeated as necessary. + +The shape of a $Zi$ satisfies two conditions. + +*Condition 1*: the number of dimensions **OF ALL OUTPUT TENSORS** is the largest number of dimensions among all the input tensors. When the number of dimensions is increased for a tensor: +- the **EXTRA** dimensions to be completed are **PREPENDED BEFORE THE** lower indexes, +- those dimensions are set to a size equal to 1, and +- the access to the tensor data remains possible. **Note 3: this remark is unclear. Is it a statement about copying the data vs accessing by reference? Or is it a subtle reference to the Description fo Step 1 below? Is the meaning something like "during the broadcasting, the data of the input tensor remains in place", where "in place" is to be intended as the technical memory access terminology? In general, it seems the intention here is to say something about the *implementation* of the broadcasting operator, which I am not sure is the desired goal.** + +*Condition 2*: the size **OF EACH OUTPUT** dimension is equal to the maximum of the sizes of all the input tensors for that dimension after expansion. **AS A CONSEQUENCE, THE SIZE OF SOME DIMENSIONS IN AN OUTPUT TENSOR MIGHT BE LARGER THAN THAT OF THE CORRESPONDING INPUT TENSOR. IF THAT IS THE CASE, THE MISSING DATA IS FILLED BY COPYING THE ENTRIES AT INDEX 0 OF THE CORRESPONDING DIMENSION.** + +The figure **BELOW** shows an **EXAMPLE** of broadcasting two tensors, i.e. $Z0, Z1 = \text{Broadcast}(X0, X1)$. + +drawing + +The operation can be described in two steps: +- Step 1: make tensors with the same number of dimensions to ensure condition 1 +- Step 2: make the tensor dimension sizes equal to ensure condition 2 + +Let us note $Y0$, ... $YL$ the tensors obtained after step 1, with a common number of dimensions $nY$ but with still different dimension sizes. + +### Description of step 1 + +The common number of dimensions is the largest number of dimensions among the input tensors: + +$$nY = \max_{m \in [0, L]} nXm$$ + +where $nXm$ is the number of dimensions of $Xm$ the $m^{\text{th}}$ input tensor. + +The tensors with $nY > nXm$ have their dimensions prepended with $nY - nXm$ times 1. That is: + +$$\forall m \in [0,L] \forall i \in [0,nY-1] ~~~~~ dYm_i = 1 ~~~~~ \text{if} ~~~~~ i < nY - nXm, ~~~~~ dXm_{i-nY+nXm} ~~~~~ \text{otherwise}$$ + +Concerning the access to data through [tensor index](https://github.com/ericjenn/working-groups/blob/ericjenn-srpwg-wg1/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md#tensor_index) we have the following relation: + +$$Ym[i]=Xm[i']$$ + +with $i$ defined from $i'$: + +$$i= \underbrace{1,...,1},i' + \atop nY-nXm$$ + +### Description of step 2 + +Now $Y0$, ... $YL$ have a common number of dimensions but different dimension sizes. This common number of dimensions, is the number of dimensions of **ALL** output tensors **Zm**: + +$$ nZm = nY $$ + +In order to **FULLY** define the output we have to set each dimension to its maximum among all tensors and, for tensors with increased dimension, access always to the first element whatever the value of the index in this dimension. + +**FOR OUR RUNNING EXAMPLE**, The figure **BELOW** presents the tensors obtained at the end of **STEP** 1. + +drawing + +#### **SETTING** each dimension to its maximum + +**Note 4: the notation was difficult to parse, I have tried to improve it.** The dimensions $dY_0$, ... $dY_{nY-1}$ are defined as the maximum over all tensors, i.e. $dY_i = \max_{m \in [0, L] } dYm_i$ for all $i\in[0,nY-1]$ where $dYm_0$, ... $dYm_{nY-1}$ are the dimensions of tensor $Ym$. Overall, we have: + +$$\forall m \in [0,L] \forall i \in [0,nZm-1] ~~~~~ dZm_i = dY_i$$ + +#### Access to data + +Let $f(.,.,.)$ a function that provides an index value of 0 when the dimension size is not the maximum size and the current index of this dimension when the dimension size is equal to the maximum dimension size. This **FUNCTION** is defined as: + +$f(a,B,C) = a$ if $B=C$ and $f(a,B,C) = 0$ otherwise where: +- $a$ is the current index, +- $B$ is the **SIZE OF THE INPUT DIMENSION**, and +- $C$ is the **SIZE OF THE OUTPUT DIMENSION**. + +When, for a given dimension $dYm_k$ of a tensor $Ym$, $dYm_k \neq dY_k$ then $f(i_k,dYm_k,dY_k)$ is 0 whatever the value of the index $i_k$. **THIS WAY**, $Zm$ always **RELATES** to the first element of $Ym$ in the $k^{\text{th}}$ dimension. + +Then the relation between elements of **BROADCASTED** tensors $Z0,...ZL$ and tensors with common number of dimensions $Y0,...YL$ are: + +$$Zm[i]=Ym[i']$$ + +with: + +$$i=i_0,...i_{nY-1}$$ + +$$i'=f(i_0,dYm_0,dY_0),...f(i_{nY-1},dYm_{nY-1},dY_{nY-1})$$ + +**FOR OUR RUNNING EXAMPLE**, The figure **BELOW** presents the way the function $f(.,.,.)$ allows to link elements of $Z0$ and $Z1$ with respectively elements of $Y0$ and $Y1$. + +drawing + +## Error conditions + +**Note 5: the link below ("Origin") does not cover the error conditions explicitly. Examples of errors are in the numpy documentation linked therein. Are we happy with the reader having to follow two links to get to the actual information? https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules** + +The following error condition applies to **BROADCASTING**: + +| Error | Statement | Origin | +| -------- | ------- | ------- | +| `E1` | $\exists m \in [0, L], \exists i \in [0, nY-1]$ such that $dYm_i \neq dY_i$ and $dYm_i \neq 1$| https://github.com/onnx/onnx/blob/main/docs/Broadcasting.md| + +For each tensor and each dimension, the value of a dimension shall be either equal to the maximum dimension among all tensors or equal to one. If not, Error `E1` occurs. + +## Inputs + +### $Xm$ for $m \in [0,L]$: `type tensor` +**Note 6: for $m \in [0,L]$ means that there are $L+1$ input tensors. This issue is present throughout the document.** $L-1$ input tensors with possibly different shapes. + +#### Constraints + + - `[C1]` Type **CONSISTENCY** + - Statement: For any $m \in [0,L]$ tensors $Xm$ and $Zm$ must have the same type. **Note 7: the shape might change, so is it really the same "type"? Maybe we can say "the entries of Xm and Zm must have the same numerical type"** + +## Outputs + +### $Zm$ for $m \in [0,L]$: `type tensor` + +**Note 8: see note 6** $L-1$ output tensors with identical shapes. + +#### Constraints + + - `[C1]` Type **CONSISTENCY** + - Statement: see constraint [[C1]](#C1ra) on tensor $Xm$. + +## Attributes + +No attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Not applicable. I.e. the data values of the output tensors are stricly equal to the corresponding data values of the input tensors. + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md b/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md new file mode 100644 index 00000000..b06a2e5e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/definitions.md @@ -0,0 +1,19 @@ +# Definitions + +- **Tensor index** (or **multi-index**): For an $n$-dimensional tensor $T$, i.e., a tensor of rank $n$, a single element is addressed by an index tuple $i=(i_0,i_1,\dots,i_{n-1})$ where $i_k$ is the index along axis $k$. We have $i_k \in [0,dT_k]$ where $dT_k$ is the number of elements along the $k$-th axis. + +- **Empty tensor (null tensor)**: A tensor with at least one zero dimension. +A null tensor can be created by operators such as **Slice** or **Where**. + >Nota: If the application of an operator leads to a tensor with a dimension equal to 0, the tensor is null and its values are no more accessible. In particular, no structural operator can be applied recover access to the tensors values: + >- Operators **Reshape** and **Flatten** preserve the number of elements of their input tensors, so a null input tensor will generate a null output tensor. + >- Operator **Squeeze** can only remove dimensions with size 1. + + >However, using `keepdims=0`, operators **ReduceSum** (resp. **ReduceProd**) applied (e.g.) on a tensor with shape `[0,X]` will produce a tensor with X zeroes (resp. with `ReduceProd`, the result would be a vector of X ones). Note that the values of the resulting tensor do not depend on the values of the initial tensor. They are the identity values for the addition (resp. multiplication). Additionally, the multiplication of two null tensors (e.g. using the **Matmul** operator) can also produce a non-null tensor with all values equal to zero.
+ In practice, this means that, if one dimension of a tensor becomes 0, the tensor cannot be reduced to some canonical null tensor (e.g., a tensor with only one dimension equal to 0) since its shape still matters. +- **Scalar**: A 0-rank tensor. +- **Vector**: A 1-rank tensor. +- **Matrix**: A 2-rank tensor. +- **Shape of a tensor**: The shape of a tensor is a list of its dimensions. The list is empty for a scalar tensor. +- Symbol `minfloat16`, `minfloat`, `mindouble` (resp. `maxfloat16`, `maxfloat`, `maxdouble`) represent the minimum (resp. maximum) values for `float16`, `float`, `double`, `respectively`. +- Symbol `minint8`, `minint16`, `minint32`, `minint64`(resp. `maxint8`, `maxint16`, `maxint32`, `maxint64`) represent the minimum (resp. maximum) values for `int8, `int16`, `int32`, `int64. +- Symbol `minint` (resp. `maxint`) represent minimum (resp. maximum) values for the integer type considered in the context where it used. diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md b/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md new file mode 100644 index 00000000..9f1cb61b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md @@ -0,0 +1,8 @@ +# General restrictions + +The following restrictions apply to all operators of the SONNX profile. + +- `[GR1]` Tensors of class `SparseTensor` are not supported +- `[GR2]` All numerical types must be indicated explicitly (no type inference). +- `[GR3]` No implicit data type conversion. +- `[GR4]` No default values. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/common/notations.md b/safety-related-profile/sonnx/ops/spec/informal/common/notations.md new file mode 100644 index 00000000..1713d943 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/common/notations.md @@ -0,0 +1,8 @@ +# Notations + +## Floating point values + +- IEEE 754 plus and minus zeroes: `+0`, `-0`, `0` is used to represent either `+0` or `-0` +- IEEE 754 plus and minus infinities: `+Inf`, `-Inf` +- IEEE 754 NaN: `NaN` + diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/concat/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/assets/imgs/.gitkeep b/safety-related-profile/sonnx/ops/spec/informal/concat/assets/imgs/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/assets/imgs/Concat_example.png b/safety-related-profile/sonnx/ops/spec/informal/concat/assets/imgs/Concat_example.png new file mode 100644 index 00000000..7d3a7c26 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/concat/assets/imgs/Concat_example.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/concat.md b/safety-related-profile/sonnx/ops/spec/informal/concat/concat.md new file mode 100644 index 00000000..2b55b011 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/concat/concat.md @@ -0,0 +1,162 @@ + +### Contents + +- `concat` [operator (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, FP64, BFLOAT16, STRING, BOOL)](#types) + +Based on ONNX documentation version 13. + + +## `concat` (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, FP64, BFLOAT16, STRING, BOOL) + +### Signature + +$Y = \text{concat}(X_{0}, \dots, X_{n})$ + +where + +- [T1] $X_{0}, \dots ,X_{n}$ input tensors with $n \in [0, 2^{31}-1[$ +- `Y`: output concatenated tensor + +#### Restrictions + +The following restriction applies to the `concat` operator for the SONNX profile: + + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `[R1]` | Attribute `axis` is positive. | Transient | + + + +#### Informal specification + +The `concat` operator concatenates the input tensors $X_{0}, \dots , X_{n}$ along the `axis` into a single output tensor `Y`. The operator `concat` is not commutative so the input tensors order impacts on the output tensor. + +Let $a$ be the concatenation axis and $dX_{k,a}$ ([see traceability tag [T6]](#T6)) the dimension of the $X_{k}$ along the axis $a$. The mathematical definition of the operator `concat` is given hereafter. + + + +$$\begin{gathered} + \texttt{[T2]} \text{ } Y[i_{0}, \dots , i_{r-1}] = X_{k}[i_{0}, \dots, i_{a}', \dots, i_{r-1}], \text{ } \texttt{[T3]} \text{ if } \text{ } \exists k \in [0, n], \text{ } s_k \le i_a \le s_k + dX_{k,a} +\end{gathered}$$ + + +Where + +- $k$ ([see traceability tag [T3]](#T3)) refers to the unique index of the source input tensor. Since there is always at least one input tensor ([see traceability tag [T1]](#T1)), $k$ is guaranteed to exist and is found by the condition: + +```math + \texttt{[T4]} \text{ } s_k \leq i_{a} < s_k + dX_{k,a} +``` +- $i_{a}'$ the local index in $X_{k}$ corresponding to the global index $i_{a}$ along dimension $a$, is defined as follows: + +```math + \texttt{[T5]}\mathord{:} \text{ } i_{a}' = i_{a} - s_k +``` +- with $s_k$ the cumulative offset along axis before input $X_{k}$ defined as: + +```math +\texttt{[T6]} \text{ } s_k= \sum_{j=0}^{k-1} dX_{j,a} +``` + +The following example illustrates the concept explained above: + +![Concat example 1](./assets/imgs/Concat_example.png) + +Let's compute the concatenation illustrated by the example above: +```math +Y = \text{concat}(X_0, X_1, X_2) \text{ along axis}=1 +``` +Now, let's calculate for \( $i_a = 3$ \): +```math +Y[0, 3] = X_k[0, 3 - s_k] +``` +According to the inequality ([see traceability tag [T4]](#T4)): +```math +s_k \leq i_a < s_k + dX_{k,a} +``` +We check for which \( $k$ \) this holds ([see traceability tag [T3]](#T3)): +```math +s_1 \leq i_a < s_1 + dX_{1,a} \Rightarrow 3 \leq 3 < 3 + 2 = 5 +``` +Thus, +```math +k = 1 +``` +So, +```math +Y[0,3] = X_1[0, 3 - s_1] = X_1[0, 3 - 3] = X_1[0, 0] +``` + +You can find more examples in [tests](./tests/.) folder. + + +#### Error conditions +No error conditions since there is no computation for `concat` operator. + +#### Inputs + + +##### **$X_{0},...,X_{n}$** (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, FP64, BFLOAT16, STRING, BOOL) + +`concat` operator accepts a variable number of input tensors. + + +[T7] All inputs must have the same total count of dimensions. Dimension sizes must match on all axes other than the concatenation axis ([see traceability tag [T8]](#T8)). + +##### Constraints + +- `[C1]:` Limit on argument number + - Statement: The number of input tensors must range from [1, $2^{31}-1$]. +- `[C2]:` Shape consistency + - Statement: All tensors must have the same shape except for the concatenation axis, i.e, + + +```math + \texttt{[T8]} \text{ } \forall i,k \text{ and all } j \neq a: dX_{i,j} = dX_{k,j} +``` +- `[C3]:` Limit on scalars + - Statement: All input tensors must be non-scalar, meaning they must have a number of dimensions of at least one. + +```math + \forall i,k : \sum dX_{i,j} \in [1, 2^{31}-1] +``` + +#### Attributes + +##### `axis`: int (required) +Attribute `axis` determines the axis along which concatenation should done. + +##### Constraints + +- `[C1]:`Valid axis domain + - Statement: `axis` must be an integer identifying a valid dimension. +```math + \forall i,k \text{ axis } \in [0, \sum dX_{i,j}-1] +``` + +#### Output + +##### `Y` (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, FP64, BFLOAT16, STRING, BOOL) + +Tensor `Y` is the output tensor of the concatenation. + +##### Constraints + +- `[C1]:` Dimension of the concatenation axis + - Statement: Output tensor must have the same shape as input tensors except for the concatenation axis where this dimension is the sum of the dimensions of the inputs i.e, + +```math + \forall i,k : r = \sum dX_{i,j}, \text{ } \texttt{[T9]} \text{ } shape(Y) = (dX_0,dX_1, \dots, dX_{r-1}) +``` +```math +dX_j = \sum_{i=1}^{n} dX_{i,j} \text{ if } j=a \text{ and } d_{j} = d_{1,j} \text{ otherwise } +``` + + +#### Formal specification +The formal specification of the `concat` operator using the Why3 language is provided in the folder [why3](./why3/.). This specification ensures the consistency and desired behavior of the operator within the constraints described. + +#### Numerical Accuracy +`concat` operator does not perform numerical operations thus numerical accuracy issues are not considered here. + diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/README.md new file mode 100644 index 00000000..f7e39f05 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/README.md @@ -0,0 +1,6 @@ +This directory contains the reviews of the **CONCAT operator** description. + +| Id | Who | +|----------|---------------------------| +|Review #1 | [Eric1](review1_eric.md) | +|Review #2 | [Eric2](review2_eric.md) | \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_1.png b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_1.png new file mode 100644 index 00000000..647c1219 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_1.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_2.png b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_2.png new file mode 100644 index 00000000..71da11bf Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/interval_2.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/output.png b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/output.png new file mode 100644 index 00000000..28f15fd2 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/imgs/output.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review-joao-ricardo.md b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review-joao-ricardo.md new file mode 100644 index 00000000..46fc88c6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review-joao-ricardo.md @@ -0,0 +1,62 @@ +# Review of the Concat informal spec + +## Concat + +### Input interval tensors inconsistencies +**Two different** intervals are suggested for the **number of input tensors**. + +Which one should be use: +- First definition (**on Signature**) + +![interval](imgs/interval_1.png) + +- Second Definition (**on input constraints**) + +![interval](imgs/interval_2.png) + +### Doubts on mathematical definitions +In this context we would like to know what does $dX_{i,j}$ mean. + +Assuming that what is in the guidelines is correct and we understood it: +- $X_{i}$ - represents tensor the ith X tensor + +- $dX_{i, j}$ - represents the dimension of $X_i$ along the j-axis (i.e the lenght of the j-axis) + +#### Constraint axis [C1] +The formula stated here says: +```math +\forall i,k \text{ axis } \in [0, \sum dX_{i,j}-1] +``` +The first thing we would like to highlight is that we are: +- **Quantifying** over **k** which is **never used** + +- **Using j** which is **never quantified** + +Furthermore, from our point of view and according to the previous assumption: +- $\sum dX_{i,j}$ - **iterates over all tensors and all axis** and **sums the length of the respective axis** +This means that an axis can take any value between **0** and the **total number of entries in all tensors combined**. +Are we missunderstooding something? +Should'nt the range of the axis be as follows: +```math +\forall i,j \text{ axis } \in [0, len(X_{i})-1] +``` +- $len(X_{i})$ - What we want to express with this is the **number of dimensions** (**length of the shape** of **$X_{i}$**) + +**Example:** +X = [ [0,0,0], + [0,0,0], + [0,0,0] + ] + +X.shape = (3, 3) + +len(X) = 2 + +#### Constraint Output [C1] +![output](imgs/output.png) + +Once again we are **using unquantified variables**. +Moreover we believe that **r** stands for **rank**. +If so, and given the last doubt we don't understand how the rank is calculated. + +In the second equation we think that the **left side** should be $dY_{j}$ instead of $dX_{j}$. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review1_eric.md b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review1_eric.md new file mode 100644 index 00000000..1ba80409 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review1_eric.md @@ -0,0 +1,81 @@ +# Eric1 +- I think that the concept of "heterogenous" is neither necessary nor clear. \ +=> We should probably stick to an explicit list of types. Which is actually what you are doing. + +In the signature of CONCAT: +- There is a problem with our convention for tags / tensors. Here, `T1` could be considered as a tensor, while it is a tag. +- Shouldn't $X_i$ be written `Xi` ? (this may be discussed, but we have to be consistent). \ +=> To be discussed. The fact that the tags are not in "[]" is also a problem when we want to use them in formulae: the tag are not clearly visible... + +Restrictions: +- "Attribute axis lower bound is restricted to 0." \ + => This is convoluted (and unclear)! use "Attribute axis is positive". + +- "dim(inputs)" \ +=> what is "dim"? what is "inputs"? (I come back to this later) \ +=> Insert line break line before "let a". + + +- "Let $a$ be the concatenation axis and $d_{k,a}$ `T5` the dimension of the $X_{k}$ input tensor $k$ along the axis $a$." \ +=> As far as possible, avoir introducing aliases to inputs, output, etc. (may be useful here, but to be checked).\ +=> "the dimension of input tensor $X_{k}$ along the axis $a$." + +- "The formal specification is given in section Formal specification below."\ +=> To be suppressed since this is part of the standard operator description structure. + +"Y shape matches the input shapes, except along the axis dimension..."\ +=> This is a constraint. Here, we should only see definitions. Constraints will be given later. + +- $i_{0}$ and $i_{r-1}$ are the indices which access respectively the first and last dimensions of a tensor to uniquely identify an element. \ +=> I don't think that this is necessary since is it clear in the formula. I thinks that it complicates the reading. + +- The "prime" on $i_a$ seems to be misplaced (detail) + +- In the formula, is it $\exists k$ or "for k such that"? Is it possible for k not to exist? + +- Eventually, the picture should be done with drawio that allows using latex formulae. + +- What is a "local tensor" in "local tensor X_k". \ + => Simply use "X_k". + +- I am not sure that it is useful to add tag on tensors (ex. tag T3 on k). \ +=> In the guidelines for the formal spec, we may ask the names to be maintained between the informal and formal spec. + +- Introduce the figure. + +- "You can find more examples in tests folder."\ +=> To be discussed (in order to uniformize the presentations of ops). + +- "a variadic list of input tensors "\ +=> This is not the list that is variadic, but the function. \ +=> I would simply write "accepts a variable number of input tensors."\ +=> would remove the "e.g., X_0,..." that brings no new info. + +"The operator concat is not commutative so the input tensors order impacts on the output tensor."\ +=> This is part of the mathematical definition. I would not write this here (otherwise, we will have to make such comments everywhere).\ +=> I would not use "heterogeneous" because it is very unclear. it could be interpreted that the different X_i could be of different types. + +- C1 is not a really a "value range" (otherwise, every condition could be considered as a "value range"). \ +=> I would be more explicit "Limit on argument number"? + +- C2: \ +=> Check the formula: the ith dimension of a tensor X is normally noted dXi. Here, we can't see the X. + +- Add a constraint about the rank of tensors: it is not possible to concat tensors of rank 0 (scalars). + +- Attribute "axis", constraint C1\ +=> Define "dim(inputs)" using the $dX$ notation. +=> Separate the constraint of being positive (whihc is a restriction) fro the constraint relating the axis to the dimension of the tensors. + +- Output, shape consistency\ +=> There are two constraints: one about the consistency with the input tensor, and one that is not exactly a consistency constraint, but one of the result of the concatenation operation. I would call the second one "dimension of the concatenation axis" or something similar. + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review2_eric.md b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review2_eric.md new file mode 100644 index 00000000..17744dc1 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/concat/reviews/review2_eric.md @@ -0,0 +1,69 @@ +# Eric2 +- About R1 + - use $A_n$ instead of $A_N$ with the underline brace. + - the explanation about $k$ is not that clear + - definition of $k$ not necessary + - indexing does not need to be defined + - not clear what is the "simplification" here. + - Clarify the condition "these input tensors..." and, if necessary, use a specific line for it. + +- Do we accept negative values for "axis"? + +- About R2: simply state that broadcasting is forbidden. + - btw, the question is not necessarily determinism because the broadcasting process is surely deterministic. What we want is a traceable and controlled processing of tensors. + +- About R3: to be clarified. shape or type? There is a specific constraint on shape (all tensors must have the same shape except on the dimension on which concatenation must be done). Thi sis a constraint, not a restriction... + +- In the signature, don't use underbrace... +- The fact that we start from 0 does make the upper bound strange, may be use $[1, 2^{31}-1[$, do not rephrase is using $n$... +- "The concat operator compute the concatenation..." => "The concat operator concatenates..." +- "Each tensor concatenated..." is not necessary => suppress. +- Une $n$ rather than $N$ because (i) this is rather usual and (ii) it prevent confusion with the $N$ of Natural numbers. +- Do not repeat "Let concat_result"... +- "the number of dimensions for the ..." => "the number **of** dimensions of the ..."? +- Its the word "rank" appropriate? For a matrix, the "rank" refers to the number of linearly independent rows/columns... use dimension only? +- In the "mathematical formula", the notation is really pythonic... I would prefer a more standard way... +- The mathematical description is overly complicated with respect to what the operator actually does. I think that it would be more appropriate to first describe the operator in siple terms with a diagram and, then, provide a more detailed explanation. +- In any case, I would not write "to simplify the formula...", but, instead, would simply remove the first part, keeping only the part where this "simplification" has been used. BTW, the simplification is not that obvious since the formulation is more of less the same (becasue we have to compute the sum of all indexes in any case...)à +- rather than "i", shoundn't we use the "axis" attribute? +- I don't know if introducing "d" is necessary... $i$ or $axis* should be sufficient. +- Item tagged "E6" should be a constraint on the input tensors. +- Same for tag "E7" whihc express a constraint on the output tensor. +- Put the "examples" in a specific section (called "examples"?) + +### Notation +Let $a$ be the concatenation axis. \ +Let $d_{ij}$ be the dimension of input tensor $X_i$ for axis $j$. + +#### Constraint +All tensors must have the same shape except for the concatenation axis, i.e, + + $\forall i, k$ and all $j ≠ a$: + $d_{ij} = d_{kj}$ + +### Shape of the output tensor ``Y`` + +$shape(Y) = (d_0, d_1, ..., d_{r-1})$ where:\ +$d_j$ = $\sum_{i=1}^n d_{ij}$ , if $j = a$ and\ +$d_j = d_{1j}$, otherwise + +### Value of of the output tensor + +Let $s_k = \sum_{j=1}^{k-1}d_{j,a}$ be the cumulative offset along axis $a$ before input $X_k$.\ +Then +$Y[i_0,...,i_{r-1}]= X_k[i_0,..., i_a-s_k,..., i_{r-1}]$ if $s_k\leq i_a \lt s_k+d_{k,a}$ + + +- The examples shall *** first *** illustrate the operator works. +- Use examples in which the values can be discriminated, e.g., 31,32,33 for tensor 3... +- Except 1 or 2, other examples shall be given at the end of the description, after the inputs, outputs, attributes... and preferably in the test notebook. + +- input tensor + - C1: "The input number of tensors..." => "The number of input tensors must be..." (note that this means that we support variadic operators) + - C2: "the shape of input tensors shall be the same except for dimension ``axis``. And see the more mathematical statement above. +- output + - C1: concept of "type to be clarified (seems to cover both "datatype" (float, int, etc.) and shape). BTW, the shape of input tensors must not be the same for all dimensions. And the shape of the output tensor shall satisfy the relation state previous (see above). + - As stated somewhere (?), we do not repeat constraints involving several inputs, outputs or attributes but provide a link to them. See operator ``conv``. + +- attributs "axis" : "Attribute axis determines how the concatenation should be operated (along which axis)." => "Attribute ``axis`` determines the axis along which concatenation should done." +- the restriction on positive value of ``axis`` shall be reflected tagged and reflected at the beginning of the file. diff --git a/safety-related-profile/sonnx/ops/spec/informal/constant/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/constant/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/constant/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/constant/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/constant/constant.md b/safety-related-profile/sonnx/ops/spec/informal/constant/constant.md new file mode 100644 index 00000000..b2b44797 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/constant/constant.md @@ -0,0 +1,255 @@ + +# Contents + +- **Constant** operator for type [real](#real) +- **Constant** operator for types [float16, float, double](#float) +- **Constant** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + + + +# **Constant** (real) + +## Signature + +Definition of operator $\text{Constant}$ signature: +$C = \textbf{Constant}(\text{value})$ + +where: + +* `value`: the constant value to fill the output tensor +* `C`: output tensor containing the constant value + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +Specific restrictions for **Constant**: + +- `[R1]` Attribute `value` must be defined. +- `[R2]` Sparse tensors are not supported. +- `[R3]` All elements in `value` must have the same type. + +## Informal specification + +Operator **Constant** generates a tensor filled with a single constant value specified by the attribute `value`. The output tensor `C` has the same shape and type as defined by `value`. + +Mathematically, for all tensor indices $i$: + +$$ +C[i] = \text{value}[i] +$$ + +### Example 1 + +```math +\text{value} = 4.2 +``` + +Result $C$: + +```math +C = \begin{bmatrix} 4.2 \end{bmatrix} +``` + +### Example 2 + +```math +\text{value} = \begin{bmatrix} 1.1 & 2.2 \\ 3.3 & 4.4 \end{bmatrix} +``` + +Result $C$: + +```math +C = \begin{bmatrix} 1.1 & 2.2 \\ 3.3 & 4.4 \end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +### `value` + +* **Description:** The constant value to fill the output tensor. +* **Type:** `tensor(real)` +* **Requirement:** Required `[R1]`. + +## Inputs + +Not applicable. **Constant** does not take any input tensors. + +## Outputs + +### `C`: real tensor + +Output tensor filled with the constant value. + +#### Constraints + +- `[C1]` Value consistency + + - Statement: The shape and type of `C` are determined by the attribute `value`. `[R1]` `[R2]` `[R3]` + + + + + +# **Constant** (float) + +where float is in {float16, float, double} + +## Signature + +$C = \textbf{Constant}(\text{value})$ + +where: + +- `value`: constant floating-point tensor +- `C`: output tensor containing the constant value + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +Specific restrictions: + +* `[R1]` Attribute `value` must be defined. +* `[R2]` Sparse tensors not supported. +* `[R3]` All elements must have the same floating-point type. + +## Informal specification + +Operator **Constant** produces a tensor of floating-point values where each element equals the corresponding value in the attribute `value`. Selection follows standard IEEE 754 semantics (NaN and infinity preserved). + +### Example 1 + +```math +\text{value} = 3.14 +``` + +Result $C$: + +```math +C = \begin{bmatrix} 3.14 \end{bmatrix} +``` + +### Example 2 + +```math +\text{value} = \begin{bmatrix} -0.0 & -\inf \\ \text{NaN} & +\inf \end{bmatrix} +``` + +Result $C$: + +```math +C = \begin{bmatrix} -0.0 & -\inf \\ \text{NaN} & +\inf \end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +### `value` + +* **Description:** Constant floating-point tensor to fill output. +* **Type:** `tensor(float16, float, double)` +* **Requirement:** Required `[R1]`. + +## Inputs + +Not applicable. + +## Outputs + +### `C`: floating-point tensor + +#### Constraints + +- `[C1]` Value consistency + + - Statement: Shape and type of `C` determined by attribute `value`. `[R1]` `[R2]` `[R3]` + + + + + +# **Constant** (int) + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature + +$C = \textbf{Constant}(\text{value})$ + +where: + +- `value`: constant integer tensor +- `C`: output tensor containing the constant value + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +Specific restrictions: + +* `[R1]` Attribute `value` must be defined. +* `[R2]` Sparse tensors not supported. +* `[R3]` All elements must have the same integer type. + +## Informal specification + +Operator **Constant** produces a tensor of integer values where each element equals the corresponding value in the attribute `value`. + +### Example 1 + +```math +\text{value} = 7 +``` + +Result $C$: + +```math +C = \begin{bmatrix} 7 \end{bmatrix} +``` + +### Example 2 + +```math +\text{value} = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} +``` + +Result $C$: + +```math +C = \begin{bmatrix} 1 & 2 \\ 3 & 4 \end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +### `value` + +* **Description:** Constant integer tensor to fill output. +* **Type:** `tensor(int8, int16, int32, int64, uint8, uint16, uint32, uint64)` +* **Requirement:** Required `[R1]`. + +## Inputs + +Not applicable. + +## Outputs + +### `C`: integer tensor + +#### Constraints + +- `[C1]` Value consistency + + - Statement: Shape and type of `C` determined by attribute `value`. `[R1]` `[R2]` `[R3]` + diff --git a/safety-related-profile/sonnx/ops/spec/informal/constant/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/constant/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/drawio/modified onnx_conv.drawio b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/drawio/modified onnx_conv.drawio new file mode 100644 index 00000000..56c75521 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/drawio/modified onnx_conv.drawio @@ -0,0 +1,7347 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/documents/conv_specification_example/imgs/onnx_conv.drawio b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/drawio/onnx_conv.drawio similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/onnx_conv.drawio rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/drawio/onnx_conv.drawio diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/3channels_std_conv_modified.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/3channels_std_conv_modified.png new file mode 100644 index 00000000..76e2550f Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/3channels_std_conv_modified.png differ diff --git a/safety-related-profile/documents/conv_specification_example/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/README.md similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/README.md rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/README.md diff --git a/safety-related-profile/documents/conv_specification_example/imgs/autopad.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/autopad.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/autopad.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/autopad.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv-dep-3-channels.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-dep-3-channels.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv-dep-3-channels.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-dep-3-channels.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv-dep.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-dep.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv-dep.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-dep.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv-std-3 channels.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std-3 channels.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv-std-3 channels.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std-3 channels.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv-std-3-channels.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std-3-channels.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv-std-3-channels.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std-3-channels.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv-std.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv-std.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv-std.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod.png new file mode 100644 index 00000000..5ec8245a Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod2.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod2.png new file mode 100644 index 00000000..ab7f699b Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_dep_3ch_mod2.png differ diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv_pad.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_pad.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv_pad.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_pad.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_pad2.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_pad2.png new file mode 100644 index 00000000..84ede13c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_pad2.png differ diff --git a/safety-related-profile/documents/conv_specification_example/imgs/conv_stride.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_stride.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/conv_stride.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_stride.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_stride3.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_stride3.png new file mode 100644 index 00000000..39928a23 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/conv_stride3.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators.png new file mode 100644 index 00000000..9db56615 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators2.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators2.png new file mode 100644 index 00000000..f0d508ce Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/convwithoperators2.png differ diff --git a/safety-related-profile/documents/profile_opset/conv/imgs/dilation.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/dilation.png similarity index 100% rename from safety-related-profile/documents/profile_opset/conv/imgs/dilation.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/dilation.png diff --git a/safety-related-profile/documents/conv_specification_example/imgs/grouped_convolution.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/grouped_convolution.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/grouped_convolution.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/grouped_convolution.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_dilationop.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_dilationop.png new file mode 100644 index 00000000..5f0c279b Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_dilationop.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop.png new file mode 100644 index 00000000..8d88edaa Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop2.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop2.png new file mode 100644 index 00000000..5109e8df Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/onnx_conv_padop2.png differ diff --git a/safety-related-profile/documents/conv_specification_example/imgs/why3_workflow.png b/safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/why3_workflow.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/imgs/why3_workflow.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/assets/imgs/why3_workflow.png diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/conv.md b/safety-related-profile/sonnx/ops/spec/informal/conv/conv.md new file mode 100644 index 00000000..05f4812a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/conv.md @@ -0,0 +1,311 @@ +# Contents + +- **Conv** operator for type [real](#real) +- **Conv** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Conv version 22](https://onnx.ai/onnx/operators/onnx__Conv.html#conv-22). + + +# **Conv** (real) + +## Signature +$Y = \textbf{conv}(X,W,[B])$ + +where: +- $X$: input tensor +- $W$: convolution kernel +- $B$: optional bias +- $Y$: output tensor + +## Restrictions +The following restrictions apply to the **conv** operator for the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Input tensor $X$ has 2 spatial axes | Transient | +| `R2` | Attribute $auto\\_pad$ is set to `NOTSET` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `R3` | Attribute $group$ is set to 1 (standard convolution) or to the number of channels of the input tensor $X$ (depthwise convolution) | Transient | + + ## Informal specification + +Operator **conv** computes the convolution of the input tensor $X$ with the kernel $W$ and adds bias $B$ to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_. + +### Standard convolution +A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where attribute $group= 1$. + +The mathematical definition of the operator is given hereafter: + +$$ +\begin{gathered} + Y[b, c, m, n] = \sum_{i=0}^{dW_1-1} \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1} \\ (X_p[b,i,m \cdot \text{strides}[0]+ j , n \cdot \text{strides}[1]+ z ] \cdot W_d[c, i, j, z]) \\ + B_b[c] +\end{gathered} +$$ + +Where +- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$ +- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$ +- $m \in [0,dY_2-1]$ is the index of the first spatial axis of output $Y$ +- $n \in [0,dY_3-1]$ is the index of the second spatial axis of output $Y$ +- $dW_1$ is the number of feature maps of kernel $W$ +- $dW_2$ is the size of the first spatial axis of kernel $W$ +- $dW_3$ is the size of the second spatial axis of kernel $W$ +- $strides$ is an attribute of the operator. It will be described later in this section. +- $X_{p} = \text{pad}(X,pads)$ is the padded version of the input tensor $X$. Function $\text{pad}$ applies zero-padding as specified by the pads attribute (see ONNX **Pad** operator). +- $W_{d} = \text{dilation}(W,dilations)$ is the dilated version of the kernel $W$. Function $\text{dilation}$ expands the kernel by inserting spaces between its elements as specified by the dilations attribute. Its definition is given later. +- $B_{b} = \text{broadcast}(B,(dY_0 , dY_1 , dY_2 , dY_3))$ is the broadcasted version of bias $B$. Function $\text{broadcast}$ replicates the bias value across the spatial dimensions and batch dimension of the output $Y$. It takes as argument the bias $B$ and the shape of output $Y$. Its definition is given later. + +The effect of the operator is illustrated on the following figure. In this example +- shape of $Y$ is ($1, 1, 4, 4$) (batch size is 1, number of data channels is 1) +- shape of $X$ is ($1, 1, 8, 8$) (batch size is 1, number of data channels is 1) +- shape of $W$ is ($1, 1, 3, 2$) (number of data channels is 1) +- shape of $B$ is ($1$) +- $pads$ is set to (2,1,2,2) (2 rows on the top, 1 column on the left, 2 rows on the bottom , 2 columns on the right) +- $dilations$ is set to (2,2) +- $strides$ is set to (3,2) + +The following figure illustrates $\text{pad}$ function applied to the input tensor $X$: +drawing + + +The following figure illustrates $\text{dilation}$ function applied to the kernel $W$: + +drawing + + +Finally, the following figure illustrates operator **Conv** applied on input $X$ with kernel $W$ and bias $B$: +drawing + +The following figure shows the case where the number of channels of $X$ is 3. In this example: +- shape of $Y$ is ($1, 1, 4, 4$) +- shape of $X$ is ($1, 1, 8, 8$) +- shape of $W$ is ($1, 1, 3, 2$) +- shape of $B$ is $1$ +- $groups$ is set to 1 +- the other attributes have the same values as in the previous figure. + +drawing + +### Depthwise convolution +A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where $group = dX_1$. + +The mathematical definition is given hereafter: + +$$ +\begin{gathered} + Y[b, c, m, n] = \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1}\\ (X_p[b, c, m \cdot \text{strides}[0] + j , n \cdot \text{strides}[1] + z \cdot ] \cdot W_d[c, 0, j , z] ) + B_b[c] +\end{gathered} +$$ + +all terms being defined as for the standard convolutions. + +The effect of the operator is illustrated on the following figure. In this example, +- shape of $Y$ is ($1, 3, 4, 4$) +- shape of $X$ is ($1, 3, 8, 8$) +- shape of $W$ is ($3, 1, 3, 2$) +- shape of $B$ is $3$ +- $groups$ is set to 3 +- the other attributes have the same values as in the previous figure. + +drawing + +## Error conditions +In the domain of real numbers, the operator has no error condition. + +## Attributes + +### $\text{strides}$: list of int + +Attribute $strides$ determines how the kernel is applied on tensor $X$ during the convolution. + +For instance, with $\mbox{\texttt{stride}}[0]=3$ and $\mbox{\texttt{stride}}[1]=2$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution. + +The effect of the $strides$ attribute is illustrated on the following figure. In this example, $strides=(3,2)$. + + + +#### Constraints +- `[C1]` Value domain + - Statement: $strides$ is a list of strictly positive integers. + - Rationale: Stride values represent the number of applications of the kernel in the two spatial dimensions +- `[C2]` Consistency between the shape of tensors $X$, $W$, $Y$ and attributes $pads$, $dilations$ and $strides$ + - Statement: + * $$\left\lfloor{\frac{alpha-((dilations[0] \cdot dW_2-1)+1)}{strides[0]}} \right\rfloor +1 = dY_2 \mbox{ with } alpha=dX_2+pads[0]+pads[2]$$ + + and + + * $$\left\lfloor{\frac{beta-((dilations[1] \cdot dW_3-1)+1)}{strides[1]}} \right\rfloor +1 = dY_3 \mbox{ with } beta=dX_3+pads[1]+pads[3]$$ + - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis. + +### $auto\\_pad$: string + +The $auto\\_pad$ attribute determines if and how automatic padding is done for the input tensor $X$. + +#### Constraints +- `[C1]` Value domain + - Statement: $auto\\_pad$ shall be in set {`NOTSET`, `VALID`, `SAME_UPPER`, `SAME_LOWER`}. +- `[C2]` Explicit padding + - Statement: $auto\\_pad$ shall be set to `NOTSET` `[R2]` + - Rationale: The SONNX profile imposes explicit padding. + +### $\text{pads}$: list of int + +Attribute $pads$ determines the padding at the beginning and end along each spatial axis of the input tensor $X$. + +$pads$ is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$. + +The padding value is 0. + +The effect of the $pads$ attribute is illustrated on the following figure. In this example, $pads=(2,1,2,2)$. + + + +#### Constraints + - `[C1]` Value domain + - Statement: $pads$ is a list of positive or null integers. + - Rationale: A padding value gives a *number of elements* to be added to some spatial axis. + - `[C2]` Consistency between the shape of $X$ and the length of $pads$ + - Statement: The length of the $pads$ list is two times the number of spatial axes of $X$ + - Rationale: Padding shall be given for all spatial axes, and a begining value and an end value must be given for each axis. + - `[C3]` Consistency between the shape of tensors $X$, $W$, $Y$ and attributes $pads$, $dilations$ and $strides$ + - Statement: see constraint [[C3]](#C2rattr1) on attribute $strides$. + +### $\text{dilations}$: list of int + +Attribute $dilations$ specifies the spacing between the kernel elements for each spatial axis of the filter $W$. The ith value in the list gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel elements are spaced out by the dilation factor for that axis. + +The value of the elements introduced by the dilation is 0. + +The effect of the $dilations$ attribute for a tensor with two spatial axes is depicted on the following figure. In this example, $dilations=(2,2)$. + + + + +#### Constraints + - `[C1]` Value domain + - Statement: $dilations$ is a list of strictly positive integers + - Rationale: The dilation is a *factor of expansion* along a certain axis. + - `[C2]` Relation between $dilations$ and $W$ + - Statement: The length of the $dilations$ list is equal to number of spatial axes of $W$. + - Rationale: Dilation is defined for all spatial axes of $W$. + - `[C3]` Consistency between the shape of tensors $X$, $W$, $Y$ and attributes $pads$, $dilations$ and $strides$ + - Statement: see constraint [[C3]](#C2rattr1) on attribute $strides$. + +### $\text{group}$: int + +This attribute specifies the number of groups the input channels and output channels are divided into. When $group=1$, a standard convolution is performed. When group is greater than 1, convolution is computed for each group separately with a specific set of filters. + +The effect of the $group$ attribute for a tensor with two spatial axes is depicted on the following figure. In this example $group=3$. + + + +(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning) + +In the example, with $group$ set to 3 and an input $X$ and an output $Y$ with 3 channels, the input and output channels will be divided into 3 groups of 1 channel. + +#### Constraints + - `[C1]` Value domain + - Statement: $group$ is a strictly positive value. + - Rationale: $group$ represent a grouping factor. + - `[C2]` Consistency between number of channels and groups + - Statement: + - $dX1 \text{ mod}$ $group= 0$ + - $dY1 \text{ mod}$ $group= 0$ + - Rationale: Each group shall have the same number of input channels and the shall be the same number of output channels assigned to each group. +- `[C3]` Support for standard and depthwise convolutions + - Statement: $group=1$ or $group=dX_1$ `[R3]` + - Rationale: SONNX only supports the most usual types of convolutions: standard ($group=1$) and depthwise convolutions $group=dX_1$ + +### $kernel\\_shape$: list of int + +This parameter specifies the shape of the convolution kernel $W$. + +#### Constraints + +- `[C1]` Value domain + - Statement: $kernel\\_shape$ is a list of strictly positive integers + - Rationale: A dimension is always positive and cannot be null. +- `[C2]` Consistency between $W$ and $kernel\\_shape$ + - Statement: The size of $W$ for an axis must bve equal to the value of $kernel\\_shape$ for that axis + - Rationale: $kernel\\_shape$ represents the shape of $W$, where $kernel\\_shape[0] = dW_3$ and $kernel\\_shape[1] = dW_2$. + + +## Inputs + +### $\text{X}$: tensor of real + +Tensor $X$ is the input tensor on which convolution with kernel $W$ is computed. + +The shape of tensor $X$ is $(dX_0 , dX_1 , dX_2 , dX_3)$, where +- $dX_0$ is the batch size of input $X$. +- $dX_1$ is the number of data channels of input $X$. +- $dX_2$ and $dX_3$ are the sizes of the input for the two spatial axes (height and width). + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $X$ + - Statement: The number of spatial axes of tensor $X$ is 2. `R1` + - Rationale: This restriction is introduced to reduce the specification effort. It matches the industrial use cases considered in the profule. +- `[C2]` Consistency between the number of channels of $X$ and $W$ + - Statement: $dW_1=\frac{dX_1}{group}$ +- `[C3]` Consistency between the shape of tensors $X$, $W$, $Y$ and attributes $pads$, $dilations$ and $strides$ + - Statement: see constraint [[C3]](#C2rattr1) on attribute $strides$. +- `[C4]` Axis denotations + - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\]. + - Rationale: Denotation convention + +### $\text{W}$: tensor of real + +Tensor $W$ is the convolution kernel. + +The shape of tensor $W$ is $(dW_0 , dW_1 , dW_2 , dW_3)$, where +- $dW_0$ is the number of output channels or number of feature maps +- $dW_1$ is the number of input channels +- $dW_2$ and $dW_3$ are the sizes of the kernel for the two spatial axes. + +#### Constraints +- `[C1]` Consistency between the number of channels of $X$ and $W$ + - Statement: see constraint [[C2]](#C2rx) on tensor $X$. +- `[C2]` Consistency between the shape of tensors $X$, $W$, $Y$ and attributes $pads$, $dilations$ and $strides$ + - Statement: see constraint [[C3]](#C3rx) on tensor $X$. +- `[C3]` Consistency between $W$ and $kernel\\_shape$ + - Statement: The size of $W$ for an axis must bve equal to the value of $kernel\\_shape$ for that axis + - Rationale: $kernel\\_shape$ represents the shape of $W$, where $kernel\\_shape[0] = dW_3$ and $kernel\\_shape[1] = dW_2$. +- `[C4]` Compliance with axis denotations + - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\]. +- `[C5]` Consistency between output channels and group + - Statement: $dW_0 \mod group == 0$ + +### $\text{B}$: tensor of real + +Tensor $B$ is the bias. + +The shape of tensor $B$ is $dB_0$. + +#### Constraints +- `[C1]` Consistency between the number of channels of $B$ and $W$ + - Statement: $dB_0 = dW_0$. + + +## Outputs + +### $\text{Y}$ : tensor of real + +The size of the output $Y$ will be $(dY_0 , dY_1 , dY_2 , dY_3)$ where +- $dY_0$ is the number of batches +- $dY_1$ is the number of channels +- $dY_2$ and $dY_3$ are the sizes of the output for the two spatial axes + +#### Constraints. +- `[C1]` Consistency between the shape of tensors $X$, $W$, $Y$, attributes $pads$ and $strides$ + - Statement: see constraint [[C3]](#C3rx) on tensor $X$. + +## Formal specification + +See the Why3 specification. + + +# **Conv** (float) +where float is in {float16, float, double} + + +## Signature diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/README.md new file mode 100644 index 00000000..f23ca249 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/README.md @@ -0,0 +1,9 @@ +This directory contains the reviews of the **CONV2D operator** description. + +| Id | Who | +|----------|---------------------------| +|Review #1 | [Sebastian](sebastian.md) | +|Review #2 | [Jean-Loup](jean-loup.md) | +|Review #3 | [Jean-Souyris](conv_review_Jean.md) | +|Review #4 | [Salome](conv_review_Salome.md) | +|Review #5 | [Joao-Ricardo](review_joao_ricardo.md) | \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/conv_review_Jean.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/conv_review_Jean.md new file mode 100644 index 00000000..76a05664 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/conv_review_Jean.md @@ -0,0 +1,301 @@ +# Jean-Souyris +> Reviewer: Jean Souyris + +> General remarks\ +> Remark 1: the traceability tags are not defined.\ +> Remark 2: dilation and broadcast are not defined.\ +> Remark 3: The conv operator should also be specified for other types than real. + +# `conv` operator +### Contents +- `Convolution` operator for type real. + +## `Conv` `(real)` + +### Signature +`Y = conv(X,W,[B])` +where +- `X`: input tensor +- `W`: convolution kernel +- `B`: optional bias +- `Y`: output tensor + +#### Restrictions +The following restrictions apply to the `conv` operator for the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Input tensor `X` has 2 spatial axes | Transient | +| `R2` | Attribute `auto_pad` is set to `NOTSET` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `R3` | Attribute `group` is set to 1 (standard convolution) or to the number of channels of the input tensor `X` (depthwise convolution) | Transient | + + #### Informal specification + +Operator `conv` computes the convolution of the input tensor `X` with the kernel `W` and adds bias `B` to the result. Two types of convolutions are supported: _standard convolution_ and _depthwise convolution_. + +##### Standard convolution +A _standard convolution_ applies a kernel (also called "filter") to the input tensor, aggregating information accross both spatial axes and channels. For a given output channel, the kernel operates accross all input channels and all contributions are summed to produce the output. This corresponds to the case where attribute `group`= 1. + +The mathematical definition of the operator is given hereafter: + +$$\begin{gathered} + Y[b, c, m, n] = \sum_{i=0}^{dW_1-1} \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1} \\ (X_p[b,i,m \cdot \text{strides}[0]+ j , n \cdot \text{strides}[1]+ z ] \cdot W_d[c, i, j, z]) \\ + B_b[c] +\end{gathered}$$ + +Where +- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output `Y` +- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output `Y` +- $m \in [0,dY_2-1]$ is the index of the first spatial axis of output `Y` +- $n \in [0,dY_3-1]$ is the index of the second spatial axis of output `Y` +- $dW_1$ is the number of feature maps of kernel `W` +- $dW_2$ is the size of the first spatial axis of kernel `W` +- $dW_3$ is the size of the second spatial axis of kernel `W` +- `strides` is an attribute of the operator. It will be described later in this section. +- $X_{p} = \text{pad}(X,pads)$ is the padded version of the input tensor `X`. Function $\text{pad}$ applies zero-padding as specified by the pads attribute (see ONNX `Pad` operator). +- $W_{d} = \text{dilation}(W,dilations)$ is the dilated version of the kernel `W`. Function $\text{dilation}$ expands the kernel by inserting spaces between its elements as specified by the dilations attribute. Its definition is given later. +- $B_{b} = \text{broadcast}(B,(dY_0 , dY_1 , dY_2 , dY_3))$ is the broadcasted version of bias `B`. Function $\text{broadcast}$ replicates the bias value across the spatial dimensions and batch dimension of the output `Y`. It takes as argument the bias `B` and the shape of output `Y`. Its definition is given later. + +The effect of the operator is illustrated on the following figure. In this example +- shape of `Y` is ($1, 1, 4, 4$) (batch size is 1, number of data channels is 1) +- shape of `X` is ($1, 1, 8, 8$) (batch size is 1, number of data channels is 1) +- shape of `W` is ($1, 1, 3, 2$) (number of data channels is 1) +- shape of `B` is ($1$) +- `pads` is set to (1,2,2,2) (1 column on the left, 2 columns on the right, 2 rows on the top, 2 rows on the bottom) +- `dilations` is set to (2,2) +- `strides` is set to (2,3) + +The following figure illustrates $\text{pad}$ function applied to the input tensor `X`: +drawing + + +The following figure illustrates $\text{dilation}$ function applied to the kernel `W`: + +drawing + + +Finally, the following figure illustrates operator `Conv` applied on input `X` with kernel `W` and bias `B`: +drawing + +The following figure shows the case where the number of channels of `X` is 3. In this example: +- shape of `Y` is ($1, 1, 4, 4$) +- shape of `X` is ($1, 1, 8, 8$) +- shape of `W` is ($1, 1, 3, 2$) +- shape of `B` is $1$ +- `groups` is set to 1 +- the other attributes have the same values as in the previous figure. + +drawing + +##### Depthwise convolution +A _depthwise convolution_ applies a specific kernel (or "filter") to each input channels. The number of output channels is equal to the number of input channels. This corresponds to the case where `group`= $dX_1$. + +The mathematical definition is given hereafter: + +$$\begin{gathered} + Y[b, c, m, n] = \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1}\\ (X_p[b, c, m \cdot \text{strides}[0] + j , n \cdot \text{strides}[1] + z \cdot ] \cdot W_d[c, 0, j , z] ) + B_b[c] +\end{gathered}$$ + +all terms being defined as for the standard convolutions. + +The effect of the operator is illustrated on the following figure. In this example, +- shape of `Y` is ($1, 3, 4, 4$) +- shape of `X` is ($1, 3, 8, 8$) +- shape of `W` is ($3, 1, 3, 2$) +- shape of `B` is $3$ +- `groups` is set to 3 +- the other attributes have the same values as in the previous figure. + +drawing + +#### Error conditions +In the domain of real numbers, the operator has no error condition. + +#### Inputs + +##### `X`: tensor of real + +Tensor `X` is the input tensor on which convolution with kernel `W` is computed. + +The shape of tensor `X` is $(dX_0 , dX_1 , dX_2 , dX_3)$, where +- $dX_0$ is the batch size of input `X`. +- $dX_1$ is the number of data channels of input `X`. +- $dX_2$ and $dX_3$ are the sizes of the input for the two spatial axes (height and width). + +###### Constraints + +- `C1`: Number of spatial axes of tensor `X` + - Statement: The number of spatial axes of tensor `X` is 2. `R1` + - Rationale: This restriction is introduced to reduce the specification effort. It matches the industrial use cases considered in the profule. +- `C2`: Consistency between the number of channels of `X` and `W` + - Statement: $dX_1=dW_1$ +- `C3`: Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` + + - Statement: + * $$\left\lfloor{\frac{alpha-(dilations[0] \cdot dW_2-1)}{strides[0]}} \right\rfloor +1 = dY_2 \mbox{ with } alpha=dX_2+pads[0]+pads[2]$$ + + and + + * $$\left\lfloor{\frac{beta-(dilations[1] \cdot dW_3-1)}{strides[1]}} \right\rfloor +1 = dY_3 \mbox{ with } beta=dX_3+pads[1]+pads[3]$$ + - Rationale: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis. +- `C4`: Axis denotations + - Statement: If axis denotation is in effect, the operation expects input data tensor to have axis denotation \[`DATA_BATCH`, `DATA_CHANNEL`, `DATA_FEATURE`, `DATA_FEATURE`\]. + - Rationale: Denotation convention + +##### `W`: tensor of real + +Tensor `W` is the convolution kernel. + +The shape of tensor `W` is $(dW_0 , dW_1 , dW_2 , dW_3)$, where +- $dW_0$ is the number of output channels or number of feature maps +- $dW_1$ is the number of input channels +- $dW_2$ and $dW_3$ are the sizes of the kernel for the two spatial axes. + +###### Constraints +- `C1`:Consistency between the number of channels of `X` and `W` + - Statement: [See constraint (C2) of X](#channel_consist). +- `C2`: Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` + - Statement: [See constraint (C3) of X](#shape_consist). +- `C3`: Consistency between `W` and `kernel_shape` + + - Statement: The size of `W` for an axis must bve equal to the value of `kernel_shape` for that axis + - Rationale: `kernel_shape` represents the shape of `W`, where `kernel_shape[0]` = $dW_3$ and `kernel_shape[1]` = $dW_2$. +- `C4`: Compliance with axis denotations + - Statement: If axis denotation is in effect, the operation expects the weight tensor to have axis denotation \[`FILTER_OUT_CHANNEL`, `FILTER_IN_CHANNEL`, `FILTER_SPATIAL`, `FILTER_SPATIAL`\]. + +##### `B` : tensor of real + +Tensor `B` is the bias. + +The shape of tensor `B` is $dB_0$. + +###### Constraints +- `C1`: Consistency between the number of channels of `B` and `W` + - Statement: $dB_0 = dW_1$. + +#### Attributes + +##### `strides`: list of int + +Attribute `strides` determines how the kernel is applied on tensor `X` during the convolution. + +For instance, with $\mbox{\texttt{stride}}[0]=2$ and $\mbox{\texttt{stride}}[1]=3$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the convolution. + +The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(2,3). + + + +###### Constraints +- `C1`: Value domain + - Statement: `strides` is a list of strictly positive integers. + - Rationale: Stride values represent the number of applications of the kernel in the two spatial dimensions +- `C2`: Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` + - Statement: [See constraint (C3) of X](#shape_consist) + +##### `auto_pad` : string + +The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X. + +###### Constraints +- `C1`: Value domain + - Statement: `auto_pad` shall be in set {`NOTSET`, `VALID`, `SAME_UPPER`, `SAME_LOWER`}. +- `C2`: Explicit padding + - Statement: `auto_pad` shall be set to `NOTSET` `[R2]` + - Rationale: The SONNX profile imposes explicit padding. + +##### `pads`: list of int + +Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor `X`. + +`pads` is a list of the form (`x1_begin`, `x2_begin`,..., `x1_end`, `x2_end`,...), where `xi_begin` is the number of elements (possibly zero) added at the beginning of axis $i$ and `xi_end` is the number of elements added at the end of axis $i$. + +The padding value is 0. + +The effect of the `pads` attribute is illustrated on the following figure. In this example, `pads`=(1,3,2,2). + + + +###### Constraints +- `C1`: Value domain + - Statement: `pads` is a list of positive or null integers. + - Rationale: A padding value gives a *number of elements* to be added to some spatial axis. +- `C2`: Consistency between the shape of `X` and the length of `pads` + - Statement: The length of the `pads` list is two times the number of spatial axes of `X` + - Rationale: Padding shall be given for all spatial axes, and a begining value and an end value must be given for each axis. +- `C3`: Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` + - Statement: [See constraint (C3) of X](#shape_consist) + +##### `dilations`: list of int + +Attribute `dilations` specifies the spacing between the kernel elements for each spatial axis of the filter `W`. The ith value in the list gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel elements are spaced out by the dilation factor for that axis. + +The value of the elements introduced by the dilation is 0. + +The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. In this example, `dilations`=(2,2). + + + + +###### Constraints +- `C1`: Value domain + - Statement: `dilations` is a list of strictly positive integers + - Rationale: The dilation is a *factor of expansion* along a certain axis. +- `C2`: Relation between `dilations` and `W` + - Statement: The length of the `dilations` list is equal to number of spatial axes of `W`. + - Rationale: Dilation is defined for all spatial axes of `W`. +- `C3`: Consistency between the shape of tensors `X`, `W`, `Y` and attributes `pads`, `dilations` and `strides` + - Statement: [See constraint (C3) of X](#shape_consist) + +##### `group`: int + +This attribute specifies the number of groups the input channels and output channels are divided into. When `group`=1, a standard convolution is performed. When group is greater than 1, convolution is computed for each group separately with a specific set of filters. + +The effect of the `group` attribute for a tensor with two spatial axes is depicted on the following figure. In this example `group`=3. + + + +(Taken from https://eli.thegreenplace.net/2018/depthwise-separable-convolutions-for-machine-learning) + +In the example, with `group` set to 3 and an input `X` and an output `Y` with 3 channels, the input and output channels will be divided into 3 groups of 1 channel. + +###### Constraints +- `C1`: Value domain + - Statement: `group` is a strictly positive value. + - Rationale: `group` represent a grouping factor. +- `C2`: Consistency between number of channels and groups + - Statement: + - $dX1 \text{ mod}$ `group` $= 0$ + - $dY1 \text{ mod}$ `group` $= 0$ + - Rationale: Each group shall have the same number of input channels and the shall be the same number of output channels assigned to each group. +- `C3`: Support for standard and depthwise convolutions + - Statement: `group`=1 or `group`$=dX_1$ `[R3]` + - Rationale: SONNX only supports the most usual types of convolutions: standard (`group`=1) and depthwise convolutions `group`$=dX_1$ + +##### `kernel_shape`: list of int + +This parameter specifies the shape of the convolution kernel `W`. + +###### Constraints. + +- `C1`: Value domain + - Statement: `kernel_shape` is a list of strictly positive integers + - Rationale: A dimension is always positive and cannot be null. +- `C2`: Consistency between `W` and `kernel_shape` + - Statement: [See constraint (C3) of W](#kernel_shape_w) + +#### Outputs + +##### `Y` : tensor of real + +The size of the output `Y` will be $(dY_0 , dY_1 , dY_2 , dY_3)$ where +- $dY_0$ is the number of batches +- $dY_1$ is the number of channels +- $dY_2$ and $dY_3$ are the sizes of the output for the two spatial axes + +###### Constraints. +- `C1`: Consistency between the shape of tensors `X`, `W`, `Y`, attributes `pads` and `strides` + - Statement: [see constraint (C3) of X](#shape_consist) + +#### Formal specification + +*(to be completed)* + diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/eric_tests.py b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/eric_tests.py new file mode 100644 index 00000000..77e65d5a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/eric_tests.py @@ -0,0 +1,488 @@ +import math +import numpy as np +import os +import json + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper, TensorProto +import onnx.checker +from onnxruntime import InferenceSession + + + + +""" +Tensor value details +""" +tensor_range = { + 'min_value': -1e6, + 'max_value': 1e6, + 'allow_nan': False, + 'allow_infinity': False +} + + +""" +Inputs/attributes details +""" +inputs_attributes = { + 'min_dx0': 1, # Dimension should always be positive (batch size > 0) + 'max_dx0': 10, # Adjust as needed + 'min_dx1': 1, # Dimension should always be positive (channels > 0) + 'max_dx1': 10, # Adjust as needed + 'x_spatial_axis_min': 1, # Dimension should always be positive (spatial axes > 0) + 'x_spatial_axis_max': 10, # Adjust as needed + 'min_dw0': 1, # Dimension should always be positive (output channels > 0) + 'max_dw0': 10, # Adjust as needed + 'w_spatial_axis_min': 1, # Dimension should always be positive (spatial axes > 0) + 'w_spatial_axis_max': 10, # Adjust as needed + 'pads_min': 0, # Pads [C1] + 'pads_max': 10, # Adjust as needed + 'strides_min': 1, # Strides [C1] + 'strides_max': 10, # Adjust as needed + 'dilation_min': 1, # Dilations [C1] + 'dilation_max': 10, # Used when auto_pad is not NOTSET + 'groups': [] +} + + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Store generated data +""" +generated_data = { + "dx0": [], + "dx1": [], + "x_spatial_axis": [], + "x": [], + "dw0": [], + "dw1": [], + "w_spatial_axis": [], + "w": [], + "db0": [], + "bias": [], + "strides": [], + "auto_pad": [], + "pads": [], + "dilations": [], + "kernel_shape": [], + "dy2": [], + "dy3": [], + "groups": [] +} + + +""" +Function to generate valid convolution arguments +""" +@settings(backend='crosshair') +@st.composite +def valid_conv_args(draw, inputs_attributes=inputs_attributes, tensor_range=tensor_range): + # A float strategy + a_float = st.floats(min_value=tensor_range['min_value'], max_value=tensor_range['max_value'], + allow_nan=tensor_range['allow_nan'], allow_infinity=tensor_range['allow_infinity']) + + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + num_spatial_axes_x = 2 + num_spatial_axes_w = 2 + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + # dx0 is the first dimension of X, i.e., the dimension of the batch size + dx0 = draw(st.integers(min_value=inputs_attributes['min_dx0'], + max_value=inputs_attributes['max_dx0'])) + + # dx1 is the second dimension of X, i.e., the number of channels + dx1 = draw(st.integers(min_value=inputs_attributes['min_dx1'], + max_value=inputs_attributes['max_dx1'])) + + # X [C1] + + x_spatial_axis = draw(st.lists( + st.integers(min_value=inputs_attributes['x_spatial_axis_min'], + max_value=inputs_attributes['x_spatial_axis_max']), + min_size=num_spatial_axes_x, + max_size=num_spatial_axes_x)) + # X [C4] + x = draw(hnp.arrays(dtype=np.float32, + shape=(dx0, dx1, *x_spatial_axis), + elements=a_float)) + + #--------------------------------------------------- + # Attribute strides + #--------------------------------------------------- + # Strides [C1] + stride_value = st.integers(min_value=inputs_attributes['strides_min'], + max_value=inputs_attributes['strides_max']) + # Strides [C2] + stride_axis_number = 2 + strides = draw(st.lists(stride_value, min_size=stride_axis_number, max_size=stride_axis_number)) + + # Groups [C1] + # Groups [C2] + # Groups [C3] + all_valid_groups = [1, dx1] + groups = draw(st.sampled_from(all_valid_groups)) + inputs_attributes['groups'].append(groups) + + + #--------------------------------------------------- + # Attributes autopad, pads and dilations + #--------------------------------------------------- + # Auto Pad [C1] + all_valid_autopad = ["NOTSET", "SAME_UPPER", "SAME_LOWER", "VALID"] + auto_pad = draw(st.sampled_from(all_valid_autopad)) + + # Auto Pad [C2] + if auto_pad == "NOTSET" or auto_pad == "VALID": + if auto_pad == "NOTSET": + # Pads [C1] + pads_value = st.integers(min_value=inputs_attributes['pads_min'], + max_value=inputs_attributes['pads_max']) + # Pads [C2] + pads = draw(st.lists( + pads_value, + min_size=2*num_spatial_axes_x, + max_size=2*num_spatial_axes_x + )) + + if auto_pad == "VALID": + pads = [0 for _ in range(2 * num_spatial_axes_x)] + + # Spatial dimension calculations + alpha = pads[0] + pads[2] + x_spatial_axis[0] + beta = pads[1] + pads[3] + x_spatial_axis[1] + w_spatial_axis = [draw(st.integers(min_value=inputs_attributes['w_spatial_axis_min'], + max_value=inputs_attributes['w_spatial_axis_max'])) for i in range(num_spatial_axes_w)] + + # Dilations [C1] + # Dilations [C2] + dilations = [draw(st.integers + (min_value=inputs_attributes['dilation_min'], + max_value=inputs_attributes['dilation_max'])) for _ in range(num_spatial_axes_w)] + + theta = (dilations[0] * (w_spatial_axis[0] - 1)) + 1 + gamma = (dilations[1] * (w_spatial_axis[1] - 1)) + 1 + + + + if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + pads = [] + dilations = [] + spatial_w_dimension_values = st.integers(min_value=inputs_attributes['w_spatial_axis_min'], + max_value=inputs_attributes['w_spatial_axis_max']) + w_spatial_axis = draw(st.lists + (spatial_w_dimension_values, + min_size=num_spatial_axes_w, + max_size=num_spatial_axes_w)) + + + #--------------------------------------------------- + # Input W + #--------------------------------------------------- + + # dw0 is the first dimension of W, i.e., the number of output channels + # W [C5] + all_valid_dw0 = [i for i in range(inputs_attributes['min_dw0'], inputs_attributes['max_dw0'] + 1) if i % groups == 0] + dw0 = draw(st.sampled_from(all_valid_dw0)) + + # dw1 is the second dimension of W, i.e., the number of input channels divided by the number of groups + # W [C1] -> X [C2] #This value does not need to be generated, by X [C2] constraint + dw1 = dx1 // groups + + # W [C4] + w = draw(hnp.arrays(dtype=np.float32, shape=(dw0, dw1, *w_spatial_axis), + elements=a_float)) + + + #--------------------------------------------------- + # Input B + #--------------------------------------------------- + # B [C1] + # db0 is the first dimension of B, i.e., the number of output channels + db0 = dw0 + bias = draw(hnp.arrays(dtype=np.float32, shape=(db0,), + elements=a_float)) + + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + if auto_pad == "NOTSET" or auto_pad == "VALID": + # y spatial dimension calculations + # When auto_pad is NOTSET, pads are explicit + # W [C2] -> X [C3], Strides [C2] -> X [C3], Pads [C3] -> X [C3], Dilations [C3] -> X [C3], Y [C1] -> X [C3] + dy2 = math.floor(((alpha - (theta)) / strides[0])) + 1 + dy3 = math.floor(((beta - (gamma)) / strides[1])) + 1 + assume (dy2 > 0) + assume (dy3 > 0) + + if auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + # y spatial dimension calculations + # When auto_pad is different from NOTSET + # W [C2] -> X [C3], Strides [C2] -> X [C3], Pads [C3] -> X [C3], Dilations [C3] -> X [C3], Y [C1] -> X [C3] + dy2 = math.ceil(x_spatial_axis[0] / strides[0]) + dy3 = math.ceil(x_spatial_axis[1] / strides[1]) + + assume (dy2 > 0) + assume (dy3 > 0) + + # Kernel Shape [C1] + # Kernel Shape [C2] -> W [C3] + # This data is not need to be generated, once it is always equal to w_spatial_axis + kernel_shape = w_spatial_axis + + + return dx0, dx1, x_spatial_axis, x, dw0, dw1, w_spatial_axis, w, db0, bias, strides, auto_pad, pads, dilations, kernel_shape, dy2, dy3, groups + + +""" +Function that runs the test +""" +@settings(max_examples=1000, backend='crosshair') +@given(valid_conv_args()) +def test_conv(args): + dx0, dx1, x_spatial_axis, x, dw0, dw1, w_spatial_axis, w, db0, bias, strides, auto_pad, pads, dilations, kernel_shape, dy2, dy3, groups = args + generated_data["dx0"].append(dx0) + generated_data["dx1"].append(dx1) + generated_data["x_spatial_axis"].append(x_spatial_axis) + generated_data["x"].append(x.tolist()) + generated_data["dw0"].append(dw0) + generated_data["dw1"].append(dw1) + generated_data["w_spatial_axis"].append(w_spatial_axis) + generated_data["w"].append(w.tolist()) + generated_data["db0"].append(db0) + generated_data["bias"].append(bias.tolist()) + generated_data["strides"].append(strides) + generated_data["auto_pad"].append(auto_pad) + generated_data["pads"].append(pads) + generated_data["dilations"].append(dilations) + generated_data["kernel_shape"].append(kernel_shape) + generated_data["dy2"].append(dy2) + generated_data["dy3"].append(dy3) + generated_data["groups"].append(groups) + + y, node_def = run_onnx_conv(dx0, dx1, x_spatial_axis, x, + dw0, dw1, w_spatial_axis, w, + db0, bias,strides, auto_pad, + pads, dilations, kernel_shape, dy2, dy3, groups) + + check_constraints(x, w, auto_pad, y, dy2, + dy3, kernel_shape, bias, strides, + node_def, pads, dilations, groups) + + +""" +Function to write generated data to a json file +""" +def teardown_module(): + data = { + "titulo": "Data generated by Hypothesis for ONNX Conv Operation", + "min_dx0": inputs_attributes['min_dx0'], + "max_dx0": inputs_attributes['max_dx0'], + "dx0": generated_data["dx0"], + "min_dx1": inputs_attributes['min_dx1'], + "max_dx1": inputs_attributes['max_dx1'], + "dx1": generated_data["dx1"], + "x_spatial_axis_min": inputs_attributes['x_spatial_axis_min'], + "x_spatial_axis_max": inputs_attributes['x_spatial_axis_max'], + "x_spatial_axis": generated_data["x_spatial_axis"], + "x": generated_data["x"], + "min_dw0": inputs_attributes['min_dw0'], + "max_dw0": inputs_attributes['max_dw0'], + "dw0": generated_data["dw0"], + "w_spatial_axis_min": inputs_attributes['w_spatial_axis_min'], + "w_spatial_axis_max": inputs_attributes['w_spatial_axis_max'], + "w_spatial_axis": generated_data["w_spatial_axis"], + "w": generated_data["w"], + "db0": generated_data["db0"], + "bias": generated_data["bias"], + "strides_min": inputs_attributes['strides_min'], + "strides_max": inputs_attributes['strides_max'], + "strides": generated_data["strides"], + "auto_pad": generated_data["auto_pad"], + "pads_min": inputs_attributes['pads_min'], + "pads_max": inputs_attributes['pads_max'], + "pads": generated_data["pads"], + "dilation_min": inputs_attributes['dilation_min'], + "dilation_max": inputs_attributes['dilation_max'], + "dilations": generated_data["dilations"], + "kernel_shape": generated_data["kernel_shape"], + "dy2": generated_data["dy2"], + "dy3": generated_data["dy3"] + } + + with open("generated_data.json", "w") as f: + json.dump(data, f, indent=4) + + +""" +Function that runs the ONNX Conv operation +""" +def run_onnx_conv(dx0, dx1, x_spatial_axis, x, + dw0, dw1, w_spatial_axis, w, + db0, bias, strides, auto_pad, + pads, dilations, kernel_shape, dy2, dy3, groups): + + x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, x_spatial_axis[0], x_spatial_axis[1]]) + w_onnx = helper.make_tensor_value_info('w_onnx', TensorProto.FLOAT, [dw0, dw1, w_spatial_axis[0], w_spatial_axis[1]]) + b_onnx = helper.make_tensor_value_info('b_onnx', TensorProto.FLOAT, [dw0]) + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilations, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + group=groups, + ) + elif auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + kernel_shape=kernel_shape, + strides=strides, + auto_pad=auto_pad, + group=groups, + ) + else: #auto_pad == "VALID" + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilations, + kernel_shape=kernel_shape, + strides=strides, + auto_pad='VALID', + group=groups, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_onnx, w_onnx, b_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dw0, dy2, dy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + # Initialize tensors + x = x.reshape(dx0, dx1, x_spatial_axis[0], x_spatial_axis[1]).astype(np.float32) + w = w.reshape(dw0, dw1, w_spatial_axis[0], w_spatial_axis[1]).astype(np.float32) + b = bias.reshape((dw0,)).astype(np.float32) + + y = sess.run(None, {'x_onnx': x, 'w_onnx':w, 'b_onnx': b})[0] + + print("Output shape:", y.shape) + print("Output values:", y) + return y, node_def + + +""" +Function that defines asserts for the constraints +""" +def check_constraints(x, w, auto_pad, y, dy2, + dy3, kernel_shape, bias, strides, + node_def, pads, dilation, groups): + + + # X Constraints + # X [C1] + assert x.ndim == 4 and x.shape[0] > 0 and x.shape[1] > 0 and x.shape[2] > 0 and x.shape[3] > 0 + # X [C2] + assert x.shape[1] == w.shape[1] * groups + # X [C3] + assert y.shape[2] == dy2 and y.shape[3] == dy3 + + # W Constraints + # W [C1] -> X [C2] + # W [C2] -> X [C3] + # W [C3] + kernel_shape[0] == w.shape[2] and kernel_shape[1] == w.shape[3] + # W [C4] + assert w.ndim == 4 + assert all(dim > 0 for dim in w.shape) + # W [C5] + assert w.shape[0] % groups == 0 + + + # B Constraints + # B [C1] + assert bias.shape[0] == w.shape[0] + + # Strides Constraints + # S [C1] + assert all(s > 0 for s in strides) + # S [C2] + assert len(strides) == 2 + # S [C2] -> X [C3] + + + # Auto_pad Constraints + # auto_pad [C1] + all_valid_autopad = ["NOTSET", "SAME_UPPER", "SAME_LOWER", "VALID"] + assert auto_pad in all_valid_autopad + # auto_pad [C2] + if auto_pad != "NOTSET": + assert all(attr.name!="pads" for attr in node_def.attribute) + + if auto_pad == "NOTSET": + # Pads Constraints + # Pads [C1] + assert all(p >= 0 for p in pads) + # Pads [C2] + assert len(pads) == (x.ndim - 2) * 2 + # Pads [C3] -> X [C3] + + # Dilation - Constraints + # Dilation [C1] + assert all(d > 0 for d in dilation) + # Dilation [C2] + assert len(dilation) == (w.ndim - 2) + # Dilation [C3] -> X [C3] + + # Groups - Constraints + # Groups [C1] + assert groups > 0 + # Groups [C2] + assert x.shape[1] % groups == 0 + assert y.shape[1] % groups == 0 + # Groups [C3] + assert groups == 1 or groups == x.shape[1] + + # Kernel shape - Constraints + # Kernel shape [C1] + assert all(k > 0 for k in kernel_shape) + # Kernel shape [C2] -> W [C3] + + # Y - Constraints + # Y [C1] -> X [C3] diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/bias-testing.py b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/bias-testing.py new file mode 100644 index 00000000..9d59c622 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/bias-testing.py @@ -0,0 +1,71 @@ +# @title Standard convolution (3 channels) +import numpy +from onnx import * +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + +# Create inputs +x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [1, 1, 2, 2]) +w = helper.make_tensor_value_info('w', TensorProto.FLOAT, [2, 1, 1, 1]) +b = helper.make_tensor_value_info('b', TensorProto.FLOAT, [2]) + +# Create a node (Conv) with input/outputs +node_def = helper.make_node( + 'Conv', # node name + ['x', 'w', 'b'], # inputs + ['y'], # outputs + dilations=[1,1], + kernel_shape=[1,1], + pads=[0, 0, 0, 0], + strides=[1, 1], + auto_pad='NOTSET', + group=1, # Standard convolution +) + +# Create the graph +graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x, w, b], + [helper.make_tensor_value_info('y', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = numpy.ones((1, 1, 2, 2), dtype=numpy.float32) +w = numpy.ones((2, 1, 1, 1), dtype=numpy.float32) +b = numpy.array([5.0,5.0], dtype=numpy.float32) + +y = sess.run(None, {'x': x, 'w':w, 'b': b})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("W shape:", w.shape) +print("W:", w) + +print("B shape:", + b.shape) +print("B:", b) + +print("Y shape:", y.shape) +print("Y:", y) diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/convReview.py b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/convReview.py new file mode 100644 index 00000000..5a04d128 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/convReview.py @@ -0,0 +1,303 @@ +import math +import numpy +import onnx.checker +from onnx import helper, TensorProto +from onnxruntime import InferenceSession + +# * Adding Padding to the Input Matrix +def padTrasform(X,pads): + ## pads = [left,top,right,bottom] + m_Lines = len(X) + n_Cols = len(X[0]) + top = [[0 for j in range(0, n_Cols + pads[0] + pads[2])] for i in range(0, pads[1])] + bottom = [[0 for j in range(0, n_Cols + pads[0] + pads[2])] for i in range(0, pads[3])] + padded_rows = [] + for x in X: + new_row = [0]*pads[0] + list(x) + [0]*pads[2] + padded_rows.append(new_row) + final = top + padded_rows + bottom + return final + +# * Adding Dilation to the Kernel Matrix +def kernelDilation(W, dilation): + dW2 = len(W) + dW3 = len(W[0]) + x_dil = dilation[0] + y_dil = dilation[1] + + horiz_W = [] + for row in W: + new_row = [] + for j in range(dW3): + new_row.append(row[j]) + if j < dW3 - 1: + for _ in range(y_dil - 1): + new_row.append(0) + horiz_W.append(new_row) + + new_W = [] + for i in range(len(horiz_W)): + new_W.append(horiz_W[i]) + if i < len(horiz_W) - 1: + for _ in range(x_dil - 1): + new_W.append([0] * len(horiz_W[0])) + return new_W + + +""" Suggetion for Dy2 and DY3 Calculation """ +# The dilated kernel is equal to the original kernel multiplied by the dilation factor +# and subtracted by ( dilation factor minus 1 ) because no zeros are added at the last margin. +# However, the value that was already in the matrix remains +# Assuming that dilation[0] refers to rows and dilation[1] to columns + +""" +dw2 - original kernel rows +dilation[0] - dilation factor for rows + +dW2_p = dW2 * dilation[0] - (dilation[0] - 1) + +dw3 - original kernel columns +dilation[1] - dilation factor for columns + +dW3_p = dW3 * dilation[1] - (dilation[1] - 1) + +""" + +def calculateDy2(dX2,pads,dW2,strides,dilation): + alpha = dX2 + pads[1] + pads[3] + theta = dilation[0] * (dW2 - 1) + 1 + finalResult = (alpha - theta) // strides[1] + 1 + return finalResult + +def calculateDy3(dX3,pads,dW3,strides,dilation): + beta = dX3 + pads[0] + pads[2] + gamma = dilation[1] * (dW3 - 1) + 1 + finalResult = (beta - gamma) // strides[0] + 1 + return finalResult + +## *! Wrong Dy2 Calculation +def wrongDy2(dX2,pads,dW2,strides,dilation): + alpha = dX2 + pads[0] + pads[2] + theta = dilation[0] * dW2 - 1 + finalResult = math.floor((alpha - theta) / strides[0]) + 1 + return finalResult + +## *! Wrong Dy3 Calculation +def wrongDy3(dX3,pads,dW3,strides,dilation): + beta = dX3 + pads[1] + pads[3] + gamma = dilation[1] * dW3 - 1 + finalResult = math.floor((beta - gamma) / strides[1]) + 1 + return finalResult + +def standConvDef(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + dW2 = len(W) + dW3 = len(W[0]) + + dX2 = len(X) + dX3 = len(X[0]) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + print("X_p:") + printTensor(X_p) + print("W_p:") + printTensor(W_p) + + dY2 = calculateDy2(dX2,pads,dW2,strides,dilation) + dY3 = calculateDy3(dX3,pads,dW3,strides,dilation) + + print("dY2:",dY2) + print("dY3:",dY3) + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Important Change + Y[m][n] += X_p[m*strides[1]+j][n*strides[0]+z] * W_p[j][z] + + return Y + +# *! Wrong Version of their code (Sum Order) +def standConvDefWrong(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + print("X_p:") + printTensor(X_p) + print("W_p:") + printTensor(W_p) + + dY2 = 4 + dY3 = 4 + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Wrong Change + Y[m][n] += X_p[m*strides[0]+j][n*strides[1]+z] * W_p[j][z] + + return Y + +# *! Wrong Version of their code (Calculation of dY2 and dY3) +def standConvDefWrongY(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + dY2 = wrongDy2(len(X),pads,len(W),strides,dilation) + dY3 = wrongDy3(len(X[0]),pads,len(W[0]),strides,dilation) + + print("dY2:",dY2) + print("dY3:",dY3) + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Important Change + Y[m][n] += X_p[m*strides[1]+j][n*strides[0]+z] * W_p[j][z] + + return Y + + +X = [ + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1] +] + +W = [ + [1,1,1], + [1,1,1] +] + + +def printTensor(T): + for x in T: + print(x) + +""" +Correct Version +""" +Y = standConvDef(X,W) +print("\nFinal Matrix Y (Correct):") +printTensor(Y) + +""" +Wrong Version of their code (Sum Order) +""" +#Y_wrong = standConvDefWrong(X,W) +#print("\nFinal Matrix Y (Wrong):") +#printTensor(Y_wrong) + +""" +Wrong Version of their code (Calculation of dY2 and dY3) +""" +#Y_wrong = standConvDefWrongY(X,W) +#print("\nFinal Matrix Y (Wrong):") +#printTensor(Y_wrong) + +print("\n") +print("ONNX CHECK") + +"""" +ONNX CHECK +""" +x_Onnx = helper.make_tensor_value_info('x_Onnx', TensorProto.FLOAT, [1, 1, 8, 8]) +w_Onnx = helper.make_tensor_value_info('w_Onnx', TensorProto.FLOAT, [1, 1, 3, 2]) +b_Onnx = helper.make_tensor_value_info('b_Onnx', TensorProto.FLOAT, [1, 1]) + +node_def = helper.make_node( + 'Conv', + ['x_Onnx', 'w_Onnx', 'b_Onnx'], + ['y_Onnx'], + dilations=[2,2], + kernel_shape=[3,2], + pads=[1, 2, 2, 2], + strides=[2, 3], + auto_pad='NOTSET', + group=1, +) + +graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_Onnx, w_Onnx, b_Onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = numpy.ones((1, 1, 8, 8), dtype=numpy.float32) +w = numpy.ones((1, 1, 3, 2), dtype=numpy.float32) +b = numpy.ones((1, 1), dtype=numpy.float32) + +y = sess.run(None, {'x_Onnx': x, 'w_Onnx':w, 'b_Onnx': b})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("W shape:", w.shape) +print("W:", w) + +print("B shape:", + b.shape) +print("B:", b) + +print("Y shape:", y.shape) +print("Y:", y) diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesis-conv.py b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesis-conv.py new file mode 100644 index 00000000..2e4285c9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesis-conv.py @@ -0,0 +1,243 @@ +""" +Using hypothesis to generate automatic tests for conv operator (SONNX) +""" +import math +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +AUTO_PAD_OPTIONS = ["NOTSET", "VALID", "SAME_UPPER", "SAME_LOWER"] + +""" +Function to generate valid inputs/atributes for Conv operator +""" +@st.composite +def valid_conv_args(draw): + # x and w dimensions + dx0 = draw(st.integers(min_value=1, max_value=10)) + dx1 = draw(st.integers(min_value=1, max_value=10)) + dx2 = draw(st.integers(min_value=1, max_value=100)) + dx3 = draw(st.integers(min_value=1, max_value=100)) + dw0 = draw(st.integers(min_value=1, max_value=10)) + dw1 = dx1 + dw2 = draw(st.integers(min_value=1, max_value=dx2)) + dw3 = draw(st.integers(min_value=1, max_value=dx3)) + # x and w tensors + x = draw(hnp.arrays(dtype=np.float32, shape=(dx0, dx1, dx2, dx3))) + w = draw(hnp.arrays(dtype=np.float32, shape=(dw0, dw1, dw2, dw3))) + #FIXME: Should BIAS be W1 or W0? + bias = draw(hnp.arrays(dtype=np.float32, shape=(dw1,))) + + # Atributes + pads = draw(st.lists( + st.integers(min_value=0, max_value=1000), min_size=4, max_size=4) + )#FIXME: Check this Max Value + strides = draw(st.lists( + st.integers(min_value=1, max_value=1000), min_size=2, max_size=2) + ) #FIXME: Check this Max Value + auto_pad = draw(st.sampled_from(AUTO_PAD_OPTIONS)) + kernel_shape = [dw2, dw3] + + # Auxiliary variables + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + line_dilation_max = math.floor((myalpha-1) /(dw2 -1)) if dw2 > 1 else 1 + column_dilation_max = math.floor((mybeta-1) /(dw3 -1)) if dw3 > 1 else 1 + line_dilation_value = draw(st.integers(min_value=1, max_value=line_dilation_max)) + column_dilation_value = draw(st.integers(min_value=1, max_value=column_dilation_max)) + + # Atributes + dilation = [line_dilation_value, column_dilation_value] + + return x, w, bias, pads, strides, dilation, auto_pad, kernel_shape + +""" +Run ONNX runtime with generated inputs/atributes and check constraints +""" +@settings(max_examples= 1000,deadline=None) +@given(valid_conv_args()) +def test_conv(args): + print("--------------------------------------------------") + x, w, bias, pads, strides, dilation, auto_pad, kernel_shape = args + dx0, dx1, dx2, dx3 = x.shape + dw0, dw1, dw2, dw3 = w.shape + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw2 - 1)) + 1 + mygamma = (dilation[1] * (dw3 - 1)) + 1 + mydy2 = math.floor((myalpha - (mytheta)) / strides[0]) + 1 + mydy3 = math.floor((mybeta - (mygamma)) / strides[1]) + 1 + + dy2 = math.floor( (myalpha - (dilation[0] * dw2 - 1)) / strides[0] ) + 1 + dy3 = math.floor( (mybeta - (dilation[1] * dw3 - 1)) / strides[1] ) + 1 + + #FIXME: Review bias dimension + x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, dx2, dx3]) + w_onnx = helper.make_tensor_value_info('w_onnx', TensorProto.FLOAT, [dw0, dw1, dw2, dw3]) + b_onnx = helper.make_tensor_value_info('b_onnx', TensorProto.FLOAT, [dw1]) + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilation, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + group=1, + ) + else: + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + kernel_shape=[dw2, dw3], + strides=strides, + auto_pad=auto_pad, + group=1, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_onnx, w_onnx, b_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dw0, mydy2, mydy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + # Initialize tensors + x = x.reshape(dx0, dx1, dx2, dx3).astype(np.float32) + w = w.reshape(dw0, dw1, dw2, dw3).astype(np.float32) + b = bias.reshape((dw1,)).astype(np.float32) + + print("Pads", pads) + print("Strides", strides) + print("Dilation", dilation) + print("Auto_pad", auto_pad) + y = sess.run(None, {'x_onnx': x, 'w_onnx':w, 'b_onnx': b})[0] + + print("x shape:", x.shape) + #print("x:", x) + + print("w shape:", w.shape) + #print("w:", w) + + print("B shape:",b.shape) + #print("B:", b) + + print("dy2:", dy2) + print("dy3:", dy3) + print("mydy2:", mydy2) + print("mydy3:", mydy3) + print("Y shape:", y.shape) + #print("Y:", y) + + check_constraints(x, w, auto_pad, y, mydy2, + mydy3, kernel_shape, b, strides, + node_def, pads, dilation) + + +def check_constraints(x, w, auto_pad, y, mydy2, + mydy3, kernel_shape, b, strides, + node_def, pads, dilation): + + #x - Constraints + # C1 + assert x.ndim == 4 and x.shape[2] >= 0 and x.shape[3] >= 0 + # C2 + assert x.shape[1] == w.shape[1] + # C3 #FIXME: REVIEW THIS BECAUSE OF AUTO_PAD + if auto_pad == "NOTSET": + assert y.shape[2] == mydy2 and y.shape[3] == mydy3 + # C4 ?? + assert x.ndim == 4 + assert all(dim > 0 for dim in x.shape) + + + #w - Constraints + # C1 + # Same of C2 of x + # C2 + #Same of C3 of x + # C3 + kernel_shape[0] == w.shape[2] and kernel_shape[1] == w.shape[3] + # C4 + assert w.ndim == 4 + assert all(dim > 0 for dim in w.shape) + + + #B - Constraints + #C1 + assert b.shape[0] == w.shape[1] + + #Strides - Constraints + # C1 + assert all(s > 0 for s in strides) + # C2 + assert len(strides) == 2 + # And same of C3 of x + + + #Auto_pad - Constraints + #C1 + assert auto_pad in AUTO_PAD_OPTIONS + #C2 + if auto_pad != "NOTSET": + assert all(attr.name!="pads" for attr in node_def.attribute) + + + #Pads - Constraints + # C1 + assert all(p >= 0 for p in pads) + # C2 + assert len(pads) == (x.ndim - 2) * 2 + # C3 + # Same of C3 of x + + + #Dilation - Constraints + # C1 + assert all(d > 0 for d in dilation) + # C2 + assert len(dilation) == (w.ndim - 2) + # C3 + # Same of C3 of x + + + #kernel shape - Constraints + # C1 + for kernel_value in kernel_shape: + assert kernel_value > 0 + # C2 + #Same of C3 of w + + #Y - Constraints + # C1 + #Same of C3 of x + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesisReview_Joao_Ricardo.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesisReview_Joao_Ricardo.md new file mode 100644 index 00000000..1d59e929 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/hypothesisReview_Joao_Ricardo.md @@ -0,0 +1,175 @@ +# Hypothesis Review + +After some tests with hypothesis for the Conv operator we arrived to some conclusions. + +Regarding the comparison between ONNX Runtime and the ONNX reference implementation (https://github.com/onnx/onnx/blob/main/onnx/reference/ops_optimized/op_conv_optimized.py), we identified the following inconsistencies: +- Limits on pad values +- Discrepancy in output values when AutoPad = NOTSET +- Discrepancy in output shapes when AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +- Discrepancy in output values when AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) + +## 1 - Limits on Pad + +This inconsistency was detected using **Hypothesis** to generate all inputs and attributes (except for Auto_pad, which was fixed to NOTSET), while respecting all constraints on attributes and inputs as defined in the informal specification. + +The ONNX reference implementation is unable to handle cases where the padding in a specific region (top, left, bottom, right) exceeds the corresponding spatial dimension of the kernel. In other words, this occurs whenever the kernel overlaps with X_padded during convolution but does **not intersect any original values of X**. +The following image illustrates an example of this scenario. + +![Example](imgs/imagem.jpg) + +In this context, two possible scenarios were identified: + +### 1.1 - Shape Errors +Although the ONNX reference implementation fails to run, ON**NX Runtime executes successfully and returns the expected result**. +In this specific case, we used **PyTorch** as a validation tool to confirm the correctness of ONNX Runtime's output. + +``` +Example: +x = np.ones((1, 1, 2, 1)).astype(np.float32) +w = np.zeros((1, 1, 2, 1)).astype(np.float32) +b = np.zeros(1).astype(np.float32) +auto_pad = "NOTSET" +dilations = [1, 1] +group = 1 +kernel_shape = [2, 1] +pads = [0, 0, 3, 0] +strides = [1, 1] +``` + +![Example](imgs/imagem1.jpg) + + +![Example](imgs/imagem3.jpg) + + +![Example](imgs/imagem4.jpg) + + +### 1.2 - Erroneous execution due to line and column Replication +Even more concerning than the previous case is the fact that there are specific inputs for which the **ONNX reference implementation does not raise an error**, but instead **computes the convolution incorrectly**. + +We believe the error occurs because **Python allows access to list indices using negative numbers**. + +![Example](imgs/imagem5.jpg) + +- ih1 should be the index where the window begins +- ih2 should be the index where the window ends + +To determine whether this was indeed a problem with the reference implementation, we also validated the ONNX Runtime's execution using **PyTorch**. + +The following image illustrates the tested example. + +![Example](imgs/imagem6.jpg) + +``` +Example: +x = np.arange(16).reshape(1, 1, 4, 4).astype(np.float32) +w = np.ones((1, 1, 3, 2), dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +dilations = [1,1] +strides = [1,1] +pads= [4,0,0,0] +auto_pad = "NOTSET" +group = 1 +kernel_shape = [3,2] +``` + +![Example](imgs/imagem7.jpg) + +(The penultimate row is correct; however, the first row should have been entirely filled with zeros. This was not the case, as it was computed in the same way as the penultimate row.) + +![Example](imgs/imagem8.jpg) + +![Example](imgs/imagem9.jpg) + + +## 2 – Discrepancy in Output Values +This inconsistency was detected using **Hypothesis** to generate all inputs and attributes (except for Auto_pad, which was fixed to NOTSET), while respecting all constraints from the informal spec. + +In this context, the output tensors computed by **ONNX Runtime** and the **ONNX Reference Implementation** diverge for certain inputs. +We believe this may be due to **rounding differences** or **operations being performed in different orders**. + +``` +Example +x = np.full((1, 1, 4, 1), 17, dtype=np.float32) +w = np.array([ + [[[-999788.], [0.], [999785.]]], + [[[999785.], [999785.], [999785.]]]], dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +dilations = [1,1] +strides = [1,1] +pads= [0,0,0,0] +auto_pad = "NOTSET" +group = 1 +kernel_shape = [3,1] +``` + +![Example](imgs/imagem10.jpg) + +## 3 – Discrepancy in Output Shapes for AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +This inconsistency was detected using **Hypothesis**, varying Auto_pad between SAME_UPPER and SAME_LOWER, while respecting all constraints from the informal spec. + +The output dimensions from **ONNX Runtime** follow the formula: + +$$output\_shape[i] = \left\lceil \frac{input\_shape[i]}{strides[i]} \right\rceil$$ + +(see [Conv - ONNX 1.20.0 documentation](https://onnx.ai/onnx/operators/onnx__Conv.html#l-onnx-doc-conv), Auto_Pad section) +ONNX Reference Implementation did not compute this correctly, resulting in **shape errors** and **empty tensors** in some cases. + +We believe the issue stems from incorrect indexing in the reference code using indices 0 and 1 (batch size and input channels) instead of 2 and 3 (spatial dimensions). + +``` +Example +x = np.arange(10).reshape(2, 1, 1, 5).astype(np.float32) +w = np.ones((1, 1, 3, 3), dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +auto_pad = "SAME_LOWER" +dilations = [1,1] +strides = [2,2] +group = 1 +kernel_shape = [3,3] +With the fixes we propose no shape inconsistencies were detected. +``` + +![Example](imgs/imagem11.jpg) + +![Example](imgs/imagem12.jpg) + +## 4 – Discrepancy in Output Values for AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +This inconsistency was also detected using **Hypothesis**, varying Auto_pad between SAME_UPPER and SAME_LOWER, while respecting all constraints from the informal spec. + +This particular error occurs on the **ONNX Runtime side**: when the input dimension value exceeds the stride for a given axis, the convolution is not computed correctly. + + +``` +Example +x = np.array([[[[0, 1, 1, 1, 1]]]]).astype(np.float32) +w = np.ones((1, 1, 1, 1), dtype=np.float32) +auto_pad = "SAME_LOWER" +dilations = [1,1] +strides = [1,5] +group = 1 +kernel_shape = [1,1] +``` + +![Example](imgs/imagem13.jpg) + +![Example](imgs/imagem14.jpg) + + +## Questions +- It’s now clear that ONNX Runtime and ONNX Reference Implementation diverge. + +- To what extent should one be used over the other? + +- Can one be reliably used to validate the other? + +- When Auto_pad is different from NOTSET, it seems to introduce significant source of randomness. + +- We have no control over how Auto_Pad is calculated in the Runtime, and given the inconsistencies we found, it’s not safe to assume it matches the reference. + +- We were not able to found any documentation saying how the Valid option for Auto_Pad works. The only available states that: "VALID mean no padding" (ONNX version 1 for Conv Operator). However, in the reference implementation Valid is treated in the same way as SAME_UPPER. + +- Are additional constraints needed beyond those in the informal spec? + +- Given what is said above how should we continue? \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem.jpg new file mode 100644 index 00000000..42a7b3c8 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem1.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem1.jpg new file mode 100644 index 00000000..41c8dc55 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem1.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem10.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem10.jpg new file mode 100644 index 00000000..41440f05 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem10.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem11.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem11.jpg new file mode 100644 index 00000000..4a885a12 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem11.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem12.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem12.jpg new file mode 100644 index 00000000..bacb68a0 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem12.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem13.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem13.jpg new file mode 100644 index 00000000..2f4ac2b6 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem13.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem14.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem14.jpg new file mode 100644 index 00000000..304f5f4a Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem14.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem3.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem3.jpg new file mode 100644 index 00000000..fc6422a1 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem3.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem4.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem4.jpg new file mode 100644 index 00000000..ed49df31 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem4.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem5.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem5.jpg new file mode 100644 index 00000000..d3c18214 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem5.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem6.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem6.jpg new file mode 100644 index 00000000..42a7b3c8 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem6.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem7.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem7.jpg new file mode 100644 index 00000000..04660826 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem7.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem8.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem8.jpg new file mode 100644 index 00000000..d2f2b181 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem8.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem9.jpg b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem9.jpg new file mode 100644 index 00000000..2b438b2a Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/imgs/imagem9.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/padding-testing.py b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/padding-testing.py new file mode 100644 index 00000000..30c80987 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/extras_rev_ricardo-joao/padding-testing.py @@ -0,0 +1,58 @@ +""" +Pad ONNX example. +""" +import numpy as np + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +# Create inputs +x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [8, 8]) +pads = helper.make_tensor_value_info('pads', TensorProto.INT64, [4]) + +# Create a node (Pad) with input/outputs +pad_node = helper.make_node( + 'Pad', + ['x', 'pads'], + ['x_padded'], + mode='constant' +) + +# Create the graph +graph_def = helper.make_graph( + [pad_node], + 'test-pad', + [x, pads], + [helper.make_tensor_value_info('x_padded', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = np.ones((8, 8), dtype=np.float32) +pads_values = [2,1,2,2] + +x_padded = sess.run(None, {'x': x, 'pads': pads_values})[0] + +print("Output:") +print(x_padded) diff --git a/safety-related-profile/documents/conv_specification_example/reviews/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/README.md similarity index 100% rename from safety-related-profile/documents/conv_specification_example/reviews/imgs/README.md rename to safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/README.md diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/bias.png b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/bias.png new file mode 100644 index 00000000..26f4cd03 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/bias.png differ diff --git a/safety-related-profile/documents/conv_specification_example/reviews/imgs/jl-img1.png b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/jl-img1.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/reviews/imgs/jl-img1.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/jl-img1.png diff --git a/safety-related-profile/documents/conv_specification_example/reviews/imgs/jl-img2.png b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/jl-img2.png similarity index 100% rename from safety-related-profile/documents/conv_specification_example/reviews/imgs/jl-img2.png rename to safety-related-profile/sonnx/ops/spec/informal/conv/reviews/imgs/jl-img2.png diff --git a/safety-related-profile/documents/conv_specification_example/reviews/jean-loup.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/jean-loup.md similarity index 100% rename from safety-related-profile/documents/conv_specification_example/reviews/jean-loup.md rename to safety-related-profile/sonnx/ops/spec/informal/conv/reviews/jean-loup.md diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo-2.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo-2.md new file mode 100644 index 00000000..cf8a8e50 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo-2.md @@ -0,0 +1,29 @@ +# Review of the Conv informal spec + +## Conv + +### Groups +### 1. Wrong property statement +On SONNX only two types of convolution are allowed: +- **Standard** - $groups = 1$ +- **Depthwise** - $groups = dX1$ + +After testing our Hypothesis script for the Conv operator an error was being raised due to the following properties: +**X[C2]** and **W[C1]** that state: $$dX1 = dW1$$ +This errors occred whenever the value of groups was the same as $dX1$ (i.e using depthwise convolution) + +This property does not match the structure proposed by the [ONNX documentation](https://onnx.ai/onnx/operators/onnx__Conv.html#l-onnx-doc-conv). + +Actually, according to the documentation the property should state: $$dW1 = \frac{dX1}{groups}$$ + +Here is an image proving our point: + +![groups_dW1](imgs/groups_dW1.png) + + +### 2. Missing an important property +It might be important to **include** the following property in the **SONNX informal spec**: $$dW0 \text{ mod } \text{ groups } == 0$$ + +Here is an image from the documentation that states this property: + +![groups_dW0](imgs/groups_dW0.png) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo.md new file mode 100644 index 00000000..f7d4a02a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review-joao-ricardo.md @@ -0,0 +1,191 @@ +# Joao-Ricardo +# Doubts and Suggested Corrections + +### Wrong Image Representation +- According to our calculations, the [image](../imgs/convwithoperators.png) does not reflect the correct convolution for the output tensor. The error occurs for the entries: $Y[3, 1]$ and $Y[3, 2]$. Instead of $5$ we believe the value should be $4$. +- We validated it afterwards with the ONNX. + +> (eric) Exact (the result tensor should be symmetric...). To be corrected. + +### Pads +- When referred for the first time, `pads` is said to be: $\text{pads} = [\text{left}, \text{right}, \text{top}, \text{bottom}]$, although, the image suggests otherwise, namely $\text{pads} = [\text{left}, \text{top}, \text{right}, \text{bottom}]$. + +> (eric) Exact. To be corrected. + +- Additionally, on the first [image](../imgs/onnx_conv_padop.png), under the arrow it states $\text{pads} = (1, 3, 2, 2)$. According to this definition, shouldn't it be $\text{pads} = [1, 2, 2, 2] +$? + +> (eric) Exact. To be corrected. + +- See our suggestion bellow for the pads structure. + + + +### Strides +- For the strides definition, we intend that it represents the magnitude of the shift of the kernel during the convolution. According to the [image](../imgs/convwithoperators.png), strides is defined: $\text{strides} = [\text{shift between columns}, \text{shift between lines}]$. Shouldn't it be $[\text{shift between lines}, \text{shift between columns}]$? + +> (eric) Exact. The order is rows (lines) *then* columns. To be corrected. + +### Convolution Formula +- **Assuming this definition of strides:** $\text{strides} = [\text{shift between columns}, \text{shift between lines}]$, **presented in the [image](../imgs/onnx_conv_padop.png)**: + +$$ +\begin{gathered} + Y[b, c, m, n] = \sum_{i=0}^{dW_1-1} \sum_{j=0}^{dW_2-1} \sum_{z=0}^{dW_3-1} \\ + (X_p[b,i,m \cdot \text{strides}[0]+ j , n \cdot \text{strides}[1]+ z ] \cdot W_d[c, i, j, z]) \\ + + B_b[c] +\end{gathered} +$$ + +> (eric) Exact. The order is rows (lines) *then* columns. See previous remark. + + +- According to this definition, we are stating the iterations over the lines should take into account the stride of the columns (and the same for the other axis). Either the strides arrangement or the formula is wrong. If we switch the strides arrangement , then the formula is correct. +Actually, when we ran our tests, we couldn't even run the code as it said "index out of bound". +When we switched the strides for $[\text{lines}, \text{columns}]$, while using this formula, everything worked out. + + +### dY2 and dY3 Calculations +- In the constraints section, more precisely for the [See constraint (C3) of X](#shape_consist), something might be wrong since those formulas do not provide the correct method for calculating $dY2$ and $dY3$. + +--- + +#### $dY2$ Calculation + +According to this formula (and to the strides and pads showed in the image): + +$$ +dY_2 = \left\lfloor \frac{\alpha - \left(dilation[0] \cdot dW_2 - 1\right)}{strides[0]} \right\rfloor + 1 +\quad \text{where}\quad \alpha = dX_2 + pads[0] + pads[2] +$$ +- $\alpha = 8 + 1 + 2 = 11$ +- $\text{dilations}[0] \times dW2 - 1 = 2 \times 2 - 1 = 3$ +- $\left\lfloor (11 - 3) / \text{strides}[0] \right\rfloor = \left\lfloor 8 / 2 \right\rfloor = 4$ +- $dY2 = 4 + 1 = 5$ (**it should be 4**) + +#### $dY3$ Calculation + +According to this formula (and to the strides and pads showed in the image): + +$$ +dY_3 = \left\lfloor \frac{\beta - \left(dilation[1] \cdot dW_3 - 1\right)}{strides[1]} \right\rfloor + 1 +\quad \text{where}\quad \beta = dX_2 + pads[1] + pads[3] +$$ +- $\beta = 8 + 2 + 2 = 12$ +- $\text{dilations}[1] \times dW3 - 1 = 2 \times 6 - 1 = 5$ +- $\left\lfloor (12 - 5) / \text{strides}[1] \right\rfloor = \left\lfloor 7 / 3 \right\rfloor = 2$ +- $dY3 = 2 + 1 = 3$ (**it should be 4**) + +#### Error Causes +- Wrong structure for pads and strides definition +- Not taking into account that for dilations greater than 2 it is not enough to subtract only one unit + +## Suggested Corrections + +The following section states some corrections that need to be done both for the examples images and to the mathematical definitions. +These recommendations have been tested and the results can be consulted in the extras folder + +### Pads +The pads dfinition should be as follows: +$$\text{pads} = [\text{padding to top}, \text{padding to left}, \text{padding to bottom}, \text{padding to right}]$$ +In order to have **X_p** like it is represented in the [image](../imgs/onnx_conv_padop.png) the pads should be like this: +$$\text{pads} = [2, 1, 2, 2]$$ + +In order to properly correct this, the images that shows the pad structure need to be corrected. + +The description of the attribute should also be corrected. + +For a concrete validation of this consult [padding-testing](../extras/padding-testing.py) + +### Strides +The strides dfinition should be as follows: +$$\text{strides} = [\text{shift between lines}, \text{shift between collumns}]$$ +In order to have shifts like those represented in the [image](../imgs/convwithoperators.png) the strides should be like this: +$$\text{strides} = [3, 2]$$ + +In order to properly correct this, the images that show the strides structure need to be corrected. + +### Convolution Formula +According to the proposed redefinition of the strides attribute the convolution formula presented is correct and need no modification. + +### dY Calculations +As the rationale states: The size of the output is determined by the number of times the kernel can be applied on a given spatial axis. + +Therefore, we have to calculate how many times the difference between the size of the input and the size of the kernel allows a stride to be done. + +#### dY2 Calculation + +**Assuming our redefinitions for the strides and the pads** the calculation of **dY2** is as follows: +$$ +dY2 = \left\lfloor \frac{\alpha - \theta}{strides[0]} \right\rfloor + 1 \quad +\text{where}$$ +$$ +\alpha = dX2 + \text{pads}[0] + \text{pads}[2]$$ +$$\quad +\theta = (\text{dilations}[0] * (dW2 - 1)) + 1 +$$ + +For a more concrete description of the theta calculation we can think of this as a dilation of the matrix by the factor $x$ in a given axis. +- $x$ = 1: Not dilated (same number of elements in the axis) +- $x$ = 2: Each element in that axis is separated with one 0 +- $x$ = 3: Each element in that axis is separated with two 0s +- $x$ = d: Each element in that axis is separated with (d - 1) 0s +- We are adding 0s **only between** the elements already present on the matrix. Therefore: +$$ +\theta = (\text{dilations}[0] * (dW2 - 1)) + 1 +$$ + +The meaning of this calculation is as follows: +- Every element of the initial matrix, except the last, will have $(\text{dilations}[0] - 1)$ zeros added to its right. +- The last element does not receive any zeros after it, so we add $+1$ to account for its position. + +According to the proposed formula: +- $\alpha = 8 + 2 + 2 = 12$ +- $\theta = 2 * (2 - 1) + 1 = 3$ +- $\left\lfloor \frac{12 - 3}{strides[0]} \right\rfloor = \left\lfloor \frac{9}{3} \right\rfloor$ = 3 +- $dY2 = 3 + 1 = 4$ + +For instance this formula was verified for multiple inputs. + +> (eric) You are perfectly right. The formula needs to be corrected. The error comes from the what that I considered (erronoeously) that dilation=0 means no dilation where as this is the case for dil=1... + +You can check [hypothesis-conv](../extras/hypothesis-conv.py). + + +#### dY3 Calculation +For this case, we propose: +$$ +dY3 = \left\lfloor \frac{\beta - \gamma}{strides[1]} \right\rfloor + 1 \quad +\text{where}$$ +$$ +\beta = dX3 + \text{pads}[1] + \text{pads}[3]$$ +$$\quad +\gamma = (\text{dilations}[1] * (dW3 - 1)) + 1 +$$ + +According to the proposed formula: +- $\beta = 8 + 1 + 2 = 11$ +- $\gamma = 2 * (3 - 1) + 1 = 5$ +- $\left\lfloor \frac{12 - 5}{strides[1]} \right\rfloor = \left\lfloor \frac{7}{2} \right\rfloor$ = 3 +- $dY2 = 3 + 1 = 4$ + +For instance this formula was verified for multiple inputs. + +You can check [hypothesis-conv](../extras/hypothesis-conv.py). + +### Bias Constraint + +The [See constraint (C1) of B](#shape_consist) suggests that: +$$dB0 = dW1$$ + +However, what we want to say is that the number of output channels ($dW0$) is the same as the shape of B ($dB0$). + +Furthermore, the ONNX documentation says: +![bias](./imgs/bias.png) + +Therefore our suggestion would be to formalize this as follows: +$$dB0 = dW0$$ + +> (eric) Exact (again) to be corrected. + +To check this you can test the [bias-testing](../extras/bias-testing.py) changing the bias dimension and evaluating its effects on the output. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review_conv_Salome.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review_conv_Salome.md new file mode 100644 index 00000000..fb4e15c5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/review_conv_Salome.md @@ -0,0 +1,58 @@ +# Salome +**GENERAL FORMATTING** + +- The title "conv operator" should be removed. The document should begin directly with the "Contents" section. + +**INPUTS** + +- For input X: + * Constraint C1: Orthographic error, it should be "profile" instead of "profule". + * Constraint C2: Add a period (.) at the end of the statement. + * Constraint C4 Rationale: Add a period (.) at the end. + +- For input W (tensor of real): + * Add a period (.) at the end of the descriptions for dW_0 and dW_1 / dW_2. + + +**ATTRIBUTES** + +- Constraint C3 (General): + * In the statement, the word "bve" should be corrected to "be". + * Add a period (.) at the end of the statement. + +- strides: + * Constraint C2 Statement: Add a period (.) at the end. + +- auto_pad: + * Constraint C2 Statement: Add a period (.) at the end. + * For discussion: Note the reference to a restriction using "[]" in this section. + +- pads: + * Constraint C2 Statement: Add a period (.) at the end. + * Constraint C3 Statement: Add a period (.) at the end. + +- dilations: + * Constraint C1 Statement: Add a period (.) at the end. + * Constraint C3 Statement: Add a period (.) at the end. + +- group: + * Constraint C3 Statement: Add a period (.) at the end. + * For discussion: Note the reference to a restriction using "[]" in this section. + +- kernel_shape: + * Constraint C1 Statement: Add a period (.) at the end. + * Constraint C2 Statement: Add a period (.) at the end. + + + +**OUTPUTS** + +- Add a period (.) at the end of the description for each output dimension: dY_0, dY_1, and dY_2/dY_3. +- Constraint C1 Statement: Add a period (.) at the end. + + + +**MISSING SECTIONS** + +- The document is missing the "Formal specification" section. +- The document is missing the "Numerical Accuracy" section. \ No newline at end of file diff --git a/safety-related-profile/documents/conv_specification_example/reviews/sebastian.md b/safety-related-profile/sonnx/ops/spec/informal/conv/reviews/sebastian.md similarity index 100% rename from safety-related-profile/documents/conv_specification_example/reviews/sebastian.md rename to safety-related-profile/sonnx/ops/spec/informal/conv/reviews/sebastian.md diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/div/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/div.md b/safety-related-profile/sonnx/ops/spec/informal/div/div.md new file mode 100644 index 00000000..a3e21d75 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/div/div.md @@ -0,0 +1,334 @@ +# Contents + +- **Div** operator for type [real](#real) +- **Div** operator for types [float16, float, double](#float) +- **Div** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Div version 14](https://onnx.ai/onnx/operators/onnx__Div.html#div-14). + + +# **Div** (real, real) + +## Signature + +$C =\textbf{Div}(A, B)$ + +where: +- $A$: numerator +- $B$: denominator +- $C$: result of the element-wise division of $A$ by $B$ + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function + +[E_DIV_REAL_FUNC_0010]
+Operator **Div** divides tensor $A$ by tensor $B$ element-wise and stores the result in $C$. If $i$ is a [tensor index](./../common/definitions.md#tensor_index), each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = A[i]/B[i] +$$ +If $B[i] \neq 0$ otherwise $C[i]$ is not defined. + + +[END]
+ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3 & 3.3 & 5.1 \end{bmatrix} +``` + +```math +C = \frac{A}{B} = \begin{bmatrix} 6.1/3 & 9.5/3.3 & 35.7/5.1 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} + 3.7 & 4.4 \\ + 16.2 & 0.5 \\ + 25.3 & 24.8 +\end{bmatrix} +``` + +```math +B = \begin{bmatrix} + 3 & 2.2 \\ + 4.1 & 1 \\ + 5.2 & 4 +\end{bmatrix} +``` + +```math +C = \frac{A}{B} = \begin{bmatrix} + 3.7/3 & 2 \\ + 16.2/4.1 & 0.5 \\ + 25.3/5.2 & 6.2 +\end{bmatrix} +``` + +## Error conditions +No error condition. + +## Attributes +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: real tensor + +Numerator of the division. + +#### Constraints + + - `[E_DIV_REAL_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +### $\text{B}$: real tensor + +Denominator of the division. + +#### Constraints + + - `[E_DIV_REAL_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_DIV_REAL_CONSTR_A_0010](#E_DIV_REAL_CONSTR_A_0010) on tensor $A$. + - `[E_DIV_REAL_CONSTR_B_0020]` Avoid undefined behaviour + - Statement: $\forall i, B[i] \neq 0$ + +## Outputs + +### $\text{C}$: real tensor + +Tensor $C$ is the element-wise result of the division of $A$ by $B$. + +#### Constraints + + - `[E_DIV_REAL_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_DIV_REAL_CONSTR_A_0010](#E_DIV_REAL_CONSTR_A_0010) on tensor $A$. + + + +# **Div** (float, float) +where float is in {float16, float, double} + +## Signature +$C = \textbf{Div}(A, B)$ + +where + + - $A$: numerator + - $B$: denominator + - $C$: result of element-wise division of $A$ by $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function + +[E_DIV_FLOAT_FUNC_0010]
+Operator **Div** divides tensor $A$ by tensor $B$ element-wise according to IEEE 754 floating-point semantics and stores the result in output tensor $C$. If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$ + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = +\begin{cases} +A[i]/B[i] & \text{if } A[i] \text{ and } B[i] \text{ are different from 0} \\ +\pm\text{0} & \text{if } A[i]=0 \text{ and } B[i] \neq 0\\ +\pm\text{inf} & \text{if } A[i] \neq 0 \text{ and } B[i]= 0 \\ +\text{NaN} & \text{if } A[i]=0 \text{ and } B[i]=0 +\end{cases} +$$ + +In the second case, the sign of $\pm \text{0}$ is determined from the sign of $B[i]$ according to the IEEE754 rules. + +In the third case, the sign of $\pm \text{inf}$ is determined from the signs of $A[i]$ and the zero ($\pm 0$) according to the IEEE754 rules. + +[END]
+### Example 1 + +```math +A = \begin{bmatrix} 3.0 & 4.5 \\ 16.0 & 1.0 \\ 25.5 & 24.25 \end{bmatrix} +\quad +B = \begin{bmatrix} 3.0 & 2.0 \\ 4.0 & 0.0 \\ 5.0 & 4.0 \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 1.0 & 2.25 \\ 4.0 & \text{+inf} \\ 5.1 & 6.0625 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 3.25 & 4.5 \\ 16.0 & 0.0 \\ 25.5 & 24.25 \end{bmatrix} +\quad +B = \begin{bmatrix} 3.0 & 2.0 \\ 4.0 & 0.0 \\ 5.0 & 4.0 \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 1.0833 & 2.25 \\ 4.0 & \text{NaN} \\ 5.1 & 6.0625 \end{bmatrix} +``` + +## Error conditions + A value in the output tensor is NaN if the numerator and denominator are both 0 (+0.0 or -0.0 according to IEEE754). + +## Attributes + +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: floating-point tensor +Numerator of the division. + +#### Constraints + +- `[E_DIV_FLOAT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_DIV_FLOAT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: floating-point tensor +Denominator of the division. + +#### Constraints +- `[E_DIV_FLOAT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_DIV_FLOAT_CONSTR_A_0010](#E_DIV_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_DIV_FLOAT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_DIV_FLOAT_CONSTR_A_0020](#E_DIV_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: floating-point tensor + +Tensor $C$ is the element-wise result of the division of $A$ by $B$. + +#### Constraints + + - `[E_DIV_FLOAT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_DIV_FLOAT_CONSTR_A_0010](#E_DIV_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_DIV_FLOAT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_DIV_FLOAT_CONSTR_A_0020](#E_DIV_FLOAT_CONSTR_A_0020) on tensor $A$. + + + + +# **Div** (int, int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +## Signature + + $C = \textbf{Div}(A,B)$ + + where + - $A$: numerator + - $B$: denominator + - $C$: result of the element-wise division of $A$ by $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function +[E_DIV_INT_FUNC_0010]
+Operator **Div** divides tensor $A$ by tensor $B$ element-wise and stores the result in output tensor $C$. + +The result of the division is the algebraic quotient of $A[i]$ by $B[i]$ with any fractional part discarded. + +[END]
+ +### Example 1 + +```math +A = \begin{bmatrix} 6 & 5 & -35 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 3 & 3 \end{bmatrix} +``` +```math +C = \begin{bmatrix} 2 & 1 & -11 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 10 & 10 \\ 21 & 1 \\ 30 & 9 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 2 \\ 4 & 1 \\ 5 & 4 \end{bmatrix} +``` + +```math +C = \begin{bmatrix} 3 & 5 \\ 5 & 1 \\ 6 & 2 \end{bmatrix} +``` + +[/info]
+ +## Error conditions +The behaviour in case of a null denominator is implementation dependent. + +## Attributes + +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: integer tensor + +Numerator of the division. + +#### Constraints + +- `[E_DIV_INT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_DIV_INT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: integer tensor + +Denominator of the division. + +#### Constraints + +- `[E_DIV_INT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_DIV_INT_CONSTR_A_0010](#E_DIV_INT_CONSTR_A_0010) on tensor $A$. +- `[E_DIV_INT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_DIV_INT_CONSTR_A_0020](#E_DIV_INT_CONSTR_A_0020) on tensor $A$. +- `[E_DIV_INT_CONSTR_B_0030]` Definition domain + - Statement: $\forall i, B[i]\neq 0$. + +## Outputs + +### $\text{C}$: integer tensor + +Tensor $C$ is the element-wise result of the division of $A$ by $B$. + +#### Constraints + +- `[E_DIV_INT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_DIV_INT_CONSTR_A_0010](#E_DIV_INT_CONSTR_A_0010) on tensor $A$. +- `[E_DIV_INT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_DIV_INT_CONSTR_A_0020](#E_DIV_INT_CONSTR_A_0020) on tensor $A$. diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/div_acc.md b/safety-related-profile/sonnx/ops/spec/informal/div/div_acc.md new file mode 100644 index 00000000..211cec46 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/div/div_acc.md @@ -0,0 +1,74 @@ +# Numerical Accuracy + +$C_{\textit{err}} = C_{\textit{err}}^{\textit{propag}} + C_{\textit{err}}^{\textit{intro}}$. + +## Error Propagation - for information - see [guidelines](../../../docs/guidelines/accuracy.md#error-propagation) + +This section contains tight properties of $C_{\textit{err}}^{\textit{propag}}$, the propagated error, where $C$ is the tensor result of an operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $A_{\textit{err}}$). For $C = A/B$, the propagated error $C_{\textit{err}}^{\textit{propag}}$ combines contributions from both $A$ and $B$: + +- For every $I$ such that $B[I] \neq 0$ and $B[I]$ does not cross zero under perturbation: + - $|C_{\textit{err}}^{\textit{propag}}[I]| \le \left|\frac{A_{\textit{err}}[I]}{B[I]}\right| + \left|\frac{A[I]\cdot B_{\textit{err}}[I]}{B[I]^2}\right| + \mathcal{O}\left(\max(|A_{\textit{err}}[I]|, |B_{\textit{err}}[I]|)^2\right)$ + +- The complete definition of $\mathcal{O}\left(\max(|A_{\textit{err}}[I]|, |B_{\textit{err}}[I]|)^2\right)$ + is available in the [guidelines](../../../docs/guidelines/accuracy.md#error-propagation). +- If $B[I]$ and $B[I] + B_{\textit{err}}[I]$ have different signs, the bound may be unbounded (division by a near-zero denominator). + +## Error Introduction (real) + +Error introduction for real (ideal) arithmetic is null: + +- $C_{\textit{err}}^{\textit{intro}} = [0]$.* + +## Error Introduction (IEEE-754 floating-point) + +Let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. + +Floating-point division introduces rounding error bounded by $|C[i]|\times\textit{\bf u}$ +for the standard rounding mode round to nearest even, provided $\frac{|A[I]|}{|B[I]|}$ is +a normal number (or for any normal number greater or equal than $\frac{|A[I]|}{|B[I]|}$). + +- $|C_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{|A[I]|}{|B[I]|}\times\textit{\bf u}$. + +## Error Introduction (int) + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +Error introduction for int arithmetic is less than 1: + +- $|C_{\textit{err}}^{\textit{intro}}| < [1]$. + +Division by zero remains undefined and shall be prevented by input constraints. + +## Unit Verification + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor A, B; + +/* A, B symbolic initialization */ + +auto result = [&A,&B](auto I) { + return (B[I].real != 0) ? A[I] / B[I] : + /* undefined */ SymbolicDomainError::undef(); +}; + +for (auto I : A.indexes()) { + auto a = A[I]; + auto b = B[I]; + if (b.real != 0 && b.real + b.err != 0) { + auto c = result(I); + double bound = std::abs(a.err / b.real) + + std::abs(a.real * b.err / (b.real * b.real)); + assert(std::abs(c.err) <= bound + 1e-12); + } +} +``` + diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/reviews/div_acc-eric.md b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/div_acc-eric.md new file mode 100644 index 00000000..211cec46 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/div_acc-eric.md @@ -0,0 +1,74 @@ +# Numerical Accuracy + +$C_{\textit{err}} = C_{\textit{err}}^{\textit{propag}} + C_{\textit{err}}^{\textit{intro}}$. + +## Error Propagation - for information - see [guidelines](../../../docs/guidelines/accuracy.md#error-propagation) + +This section contains tight properties of $C_{\textit{err}}^{\textit{propag}}$, the propagated error, where $C$ is the tensor result of an operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $A_{\textit{err}}$). For $C = A/B$, the propagated error $C_{\textit{err}}^{\textit{propag}}$ combines contributions from both $A$ and $B$: + +- For every $I$ such that $B[I] \neq 0$ and $B[I]$ does not cross zero under perturbation: + - $|C_{\textit{err}}^{\textit{propag}}[I]| \le \left|\frac{A_{\textit{err}}[I]}{B[I]}\right| + \left|\frac{A[I]\cdot B_{\textit{err}}[I]}{B[I]^2}\right| + \mathcal{O}\left(\max(|A_{\textit{err}}[I]|, |B_{\textit{err}}[I]|)^2\right)$ + +- The complete definition of $\mathcal{O}\left(\max(|A_{\textit{err}}[I]|, |B_{\textit{err}}[I]|)^2\right)$ + is available in the [guidelines](../../../docs/guidelines/accuracy.md#error-propagation). +- If $B[I]$ and $B[I] + B_{\textit{err}}[I]$ have different signs, the bound may be unbounded (division by a near-zero denominator). + +## Error Introduction (real) + +Error introduction for real (ideal) arithmetic is null: + +- $C_{\textit{err}}^{\textit{intro}} = [0]$.* + +## Error Introduction (IEEE-754 floating-point) + +Let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\bf u} = \frac{\varepsilon}{2}$. + +Floating-point division introduces rounding error bounded by $|C[i]|\times\textit{\bf u}$ +for the standard rounding mode round to nearest even, provided $\frac{|A[I]|}{|B[I]|}$ is +a normal number (or for any normal number greater or equal than $\frac{|A[I]|}{|B[I]|}$). + +- $|C_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{|A[I]|}{|B[I]|}\times\textit{\bf u}$. + +## Error Introduction (int) + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +Error introduction for int arithmetic is less than 1: + +- $|C_{\textit{err}}^{\textit{intro}}| < [1]$. + +Division by zero remains undefined and shall be prevented by input constraints. + +## Unit Verification + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor A, B; + +/* A, B symbolic initialization */ + +auto result = [&A,&B](auto I) { + return (B[I].real != 0) ? A[I] / B[I] : + /* undefined */ SymbolicDomainError::undef(); +}; + +for (auto I : A.indexes()) { + auto a = A[I]; + auto b = B[I]; + if (b.real != 0 && b.real + b.err != 0) { + auto c = result(I); + double bound = std::abs(a.err / b.real) + + std::abs(a.real * b.err / (b.real * b.real)); + assert(std::abs(c.err) <= bound + 1e-12); + } +} +``` + diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/reviews/eric.md b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/eric.md new file mode 100644 index 00000000..2a68e21a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/eric.md @@ -0,0 +1,78 @@ +### RK 1 (status is "NOT ANALYZED") +I have remove the following text: + +> Preliminary remarks +> Types +> - Operators are first described for values in the domain of real numbers. Because the `Div` operator divides each element in the input tensors element-wise, the output is of the same data type as the inputs. The inputs `A` and `B` can be of various types including `tensor(bfloat16)`, `tensor(double)`, `tensor(float)`, `tensor(float16)`, `tensor(int16)`, `tensor(int32)`, `tensor(int64)`, `tensor(int8)`, `tensor(uint16)`, `tensor(uint32)`, `tensor(uint64)`, and `tensor(uint8)`. The dimension size of a tensor is defined by $N(tensor)$. + + +Because this concerns the general structure of all operators : +- first specify the operator in R +- then specify the operator for specific types + +### RK 2 (status is "NOT ANALYZED") +> - if Elements of tensor `B` is zero, as division by zero is not valid the nemeric result will be infinite representation. `[R5]` + +This is not a "restriction": this is a actually a contraints on the operator parameters. + + +### RK 3 (status is "CORRECTED") +> Tensor `A` is one of the two input tensors to be divided. +=> +> Tensor `A` is the numerator of the division + +Same remark for `B`. + +### RK 4 (status is "CORRECTED") +I have slightly rephrased the condition concerning the range constraint. + +I have removed the "formula" : + > $\forall B[i] \neq 0$ else the result will be inf `[R5]` +because, we ar only considering values in R. + +The formula should be written for a tensor with any dimensions... + +### RK 5 (status is "NOT ANALYZED") + +> Broadcastable tensor is forbidden. `[R4]` + +This is redundant since we have a constraint on the shape of the tensor. +Furthermore, this constraint concern the graph, not the operator. + +### RK 6 (status is "CORRECTED") + +> The mathematical definition of the operator is given hereafter + +Added : "for a unidimensional tensor." + +### RK 7 (status is "CORRECTED") + +Slightly edited formula. + +### RK 8 (status is "NOT ANALYZED") + +>Where +>- $i$ is an index covering all dimensions of the tensors. + +Strange formulation... to be discussed... I have modified to say that we are considering a unidimensional tensor. + + +### RK 9 (status is "NOT ANALYZED") + +If we give an example, we should give an example with ONNX. We said that we will provide examples in G collab. + +> Note in python it is equivalent to do : +> ```python +> >>> import numpy as np +> np.divide([[1,3],[5,7],[9,12]],[[11,22],[33,-44],[-55,66]]) +> array([[ 0.09090909, 0.13636364], +> [ 0.15151515, -0.15909091], +> [-0.16363636, 0.18181818]]) + +> with a division by 0 +> np.divide([[6,9,35]],[[3,3,0]]) +> array([[ 2., 3., inf]]) + +### RK 10 (status is "NOT ANALYZED") + +Added section for floating-point implementation types. diff --git a/safety-related-profile/sonnx/ops/spec/informal/div/reviews/jean.md b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/jean.md new file mode 100644 index 00000000..bc92e349 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/div/reviews/jean.md @@ -0,0 +1,30 @@ + + +Rmq1: links +In the text below, the link to C1 does not work. +### $B$: real +Denominator of the division. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $A$. + +It is the case of all this kind of links to previously defined constraint. + +Rmq2: Unit verification sections +How these sections will be used? Are there directly usable? +What is their added-value with respect to the mathematical formulae? + +Rmq3: Unit verification sections (if kept in the informal spec). + +The second Unit verification section should refer to the first one for what concerns the defintion of SymbolicDomainError. + +Rmq4: Unit verification sections (if kept in the informal spec). + +The following text should be replaced by a reference to the first Unit verification section: "This section contains a verification scenario to verify the above specification for any C/C++ implementation. It uses an abstract type SymbolicDomainError replacing each real number in the Why3 specification. SymbolicDomainError is a data structure with 4 fields:" + +Rmq5: Traceability tags should be defined. + + + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/exp/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/exp/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/exp/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/exp/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/exp/exp.md b/safety-related-profile/sonnx/ops/spec/informal/exp/exp.md new file mode 100644 index 00000000..6e2772e9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/exp/exp.md @@ -0,0 +1,225 @@ +# Contents + +- **Exp** operator for type [real](#real) +- **Exp** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Exp version 13](https://onnx.ai/onnx/operators/onnx__Exp.html). + + +# **Exp** (real) + +## Signature + +$Y = \textbf{Exp}(X)$ + +where: +- $X$: Input tensor +- $Y$: Exponential of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Exp** operator. + +## Informal specification + +The **Exp** operator computes the element-wise exponential function of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = e^{X[i]} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 1 & 2.71828175 & 0.36787945 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.135335281 & 1 \\ + 2.71828175 & 7.38905621 \\ + 0.018315639 & 54.5981483 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Exp** has no attribute. + +## Inputs + +### $\text{X}$: real tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: real tensor + +Exponential of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1rx). + +## Formal specification + +See the Why3 specification. + + +# **Exp** (float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Exp}$ signature: +$Y = \textbf{Exp}(X)$ + +where: +- $X$: Input tensor +- $Y$: Exponential of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Exp** operator. + +## Informal specification + +The **Exp** operator computes the element-wise exponential function of the input tensor $X$ according to IEEE 754 floating-point semantics. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i]=\text{NaN} \\ +\text{0.0} & \text{if } X[i]=\text{-inf} \\ +\text{+inf} & \text{if } X[i]=\text{+inf} ~\text{or}~X[i]> X_{max}\\ + +e^{X[i]} & \text{otherwise} \\ +\end{cases} +$$ + +The values of $X_{max}$ are defined hereafter: +- float16: $X_{max}= \ln(\text{maxfp16}) \approx 11.09375$. +- float: $X_{max}= \ln(\text{maxfp32}) \approx 88.72283935546875$. +- double: $X_{max} = \ln(\text{maxfp64}) \approx 709.782712893384$. + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 1.0 & 2.71828175 & 0.36787945 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.135335281 & 1.0 \\ + 2.71828175 & 7.38905621 \\ + 0.0183156393 & 54.5981483 +\end{bmatrix} +``` + + +### Example 3 + +```math +X = \begin{bmatrix} + \text{+inf} & \text{NaN} & \text{-inf} +\end{bmatrix} +``` + +```math +Y = \begin{bmatrix} + \text{+inf} & \text{NaN} & 0.0 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Exp** has no attribute. + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: $X$ and $Y$ shall have the same floating-point type. + +## Outputs + +### $\text{Y}$: floating-point tensor + +Exponential of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1fx). +- `[C2]` Type consistency + - Statement: See [constraint (C2) on X](#C2fx). + +## Numeric accuracy + +[See the numeric accuracy note](./exp_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/exp/exp_acc.md b/safety-related-profile/sonnx/ops/spec/informal/exp/exp_acc.md new file mode 100644 index 00000000..b843580b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/exp/exp_acc.md @@ -0,0 +1,59 @@ +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Exp** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = \exp(X)$ with $\exp(x) = e^x$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\exp$ is $d\exp(x)/dx = \exp(x)$, a first-order bound is: + +- For every index $I$: + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |\exp(X[I])|\cdot|X_{\textit{err}}[I]|$ + +There is no uniform finite global Lipschitz bound on $\exp$ over $\mathbb{R}$, since $|\exp(x)|$ grows unboundedly as $x \to +\infty$. + +### Error Introduction +Error introduction for real (ideal) arithmetic is null: +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto exp_real = [](const SymbolicDomainError &v) { + // Exp in the real domain: exp(x) = e^x + SymbolicDomainError r; + r.real = std::exp(v.real); + // float/err/rel_err are set by the abstract interpreter / analysis framework + return r; +}; + +auto result = [&X,&exp_real](auto I) { + return exp_real(X[I]); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + auto y = result(I); + + // First-order propagated error bound: + // |err_exp| <= |exp(x)| * |err_x| + double exp_x = std::exp(x.real); + double local_lipschitz = std::abs(exp_x); + double bound = local_lipschitz * std::abs(x.err); + + assert(std::abs(y.err) <= bound + 1e-12); +} +``` diff --git a/safety-related-profile/sonnx/ops/spec/informal/exp/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/exp/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/flatten/flatten.md b/safety-related-profile/sonnx/ops/spec/informal/flatten/flatten.md new file mode 100644 index 00000000..73bf47b4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/flatten/flatten.md @@ -0,0 +1,154 @@ +# Contents + +- **Flatten** operator for [real](#types) and types [float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64](#types) + +Based on ONNX documentation [Flatten version 25](https://onnx.ai/onnx/operators/onnx__Flatten.html#flatten-25). + + +# **Flatten** (type) + +where type is real or in {float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +## Signature +$Y = \textbf{Flatten}(X)$ + +where: +- $X$: Input tensor +- $Y$: Output tensor + +## Restrictions +[General Restrictions](./../common/general_restrictions.md) are applicable. + +## Informal specification + +Operator $\text{Flatten}$ reshapes the input tensor $X$ into a 2D tensor $Y$ (i.e., $rY=2$). + +The size of the first dimension of $Y$ is equal to the product of the sizes of the dimensions of $X$ from the start up to (but not including) the dimension specified by $\text{axis}$. + +The size of the second dimension of $Y$ is determined by the product of the sizes of the dimensions of $X$ from the dimension specified by $\text{axis}$ to the end. + +$$\text{dY}_{0} = \prod_{i=0}^{\text{axis'}-1} \text{dX}_{i}$$ +$$\text{dY}_{1} = \prod_{i=\text{axis'}}^{rX-1} \text{dX}_{i}$$ + +Where +- $\text{axis'}$ is the normalized $\text{axis}$ and is calculated as follows: + +$$ \begin{cases} + \text{axis'} = \text{axis} & \text{if } \text{axis} \geq 0 \\ + \text{axis'} = \text{axis} + rX & \text{if } \text{axis} < 0 +\end{cases}$$ + +Flatten operation is defined as: + + + +$$Y[a, b] = X[j_0, j_1, \ldots, j_{rX-1}]$$ + + +Where: +- $[j_0,...,j_{rX-1}]$ and $[a,b]$ are [tensor indexes](./../common/definitions.md#tensor_index) +- $a = \displaystyle\sum_{z=0}^{\text{axis'}-1} \left( j_z \prod_{k=z+1}^{\text{axis'}-1} dX_k \right)$ + +- $b = \displaystyle\sum_{z=\text{axis'}}^{rX-1} \left( j_z \prod_{k=z+1}^{rX-1} dX_k \right)$ + +Note 1: when the start index in a sum or product is greater than the end index then +- the product is defined to be 1 +- the sum is defined to be 0. + +Note 2: if $X$ is a scalar, $dX_i$ is not defined, but the value is given by applying the rule given in note 1. + + +### Example 1 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +\text{axis} = 0 +``` + +```math +Y = \begin{bmatrix}\begin{bmatrix} 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 & 21 & 22 & 23 \end{bmatrix}\end{bmatrix} +``` +### Example 2 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +\text{axis} = 1 +``` + +```math +Y = + \begin{bmatrix} 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 \\ +12 & 13 & 14 & 15 & 16 & 17 & 18 & 19 & 20 & 21 & 22 & 23 \end{bmatrix} + +``` +### Example 3 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = 2 +``` +```math +Y = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3\end{bmatrix} \begin{bmatrix} 4 & 5 & 6 & 7 \end{bmatrix} \begin{bmatrix} 8 & 9 & 10 & 11 \end{bmatrix} \begin{bmatrix} 12 & 13 & 14 & 15 \end{bmatrix} \begin{bmatrix} 16 & 17 & 18 & 19 \end{bmatrix} \begin{bmatrix} 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +## Error conditions +If any pre-conditions is not satisfied, then the behavior is undefined. + +## Attributes + +### axis: `integer` +The axis starting from which the input tensor will be flattened into the second dimension of the output. + +#### Constraints + - `[C1]` Value domain + - Statement: $\text{axis} \in [-rX, rX]$ + - Rationale: Ensures that the attribute `axis` is a valid axis for tensor $X$ + +## Inputs + +### $X$: `real` +Tensor $X$ is the input tensor to be flattened. + +#### Constraints + + - `[C1]` Consistency between the shape of tensor $X$ and attribute `axis` + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Outputs + +### $Y$: `real tensor` +Tensor $Y$ is the flattened output tensor. + +### Constraints + + - `[C1]` Shape consistency + - Statement: The shape of tensor $Y$ is $(dY_0, dY_1)$, where: + - $dY_0 = \prod_{i=0}^{\text{axis'}-1} dX_i$ + + - $dY_1 = \prod_{i=\text{axis'}}^{rX-1} dX_i$ + + Where + - $dX_i$ is the size of dimension $i$ of tensor $X$ + +## Numerical Accuracy + +The $\text{Flatten}$ operator does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. diff --git a/safety-related-profile/sonnx/ops/spec/informal/gemm/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/gemm/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/gemm/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/gemm/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/gemm/gemm.md b/safety-related-profile/sonnx/ops/spec/informal/gemm/gemm.md new file mode 100644 index 00000000..da43f1b9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/gemm/gemm.md @@ -0,0 +1,136 @@ + +# `Gemm` operator (real) + +### Restrictions +The following restrictions apply to the `Gemm` operator for the SONNX profile: +- The number of spatial axes of the tensors is restricted to 2 ========TBC======`[R1]` +- alpha, beta, transA, transB ONNX attributes are not supported. A similar behaviour can be optained using `Mul` or `Transpose` operators. ========TBC======`[R2]` +- `Gemm` is not unidirectionnaly broadcastable. ========TBC======`[R3]` +- `C` input is not optional. `MatMul` shall be used in this case. ========TBC======`[R4]` + +### Signature +`Y = Gemm(A,B,C)` +where +- `A`: first input tensor +- `B`: second input tensor +- `C`: bias input tensor +- `Y`: output tensor + +#### Informal specification + +Operator `Gemm` computes the matrix multiplication of the input tensors `A` and `B`, then add the `C` bias tensor into the output tensor `Y`. + +The mathematical definition of the operator is given hereafter. + +$$ + Y = (A \times B) + C +$$ + + +$$ + \begin{bmatrix} + y_{11} & y_{12} & \cdots & y_{1p}\\ + y_{21} & y_{22} & \cdots & y_{2p}\\ + \vdots & \vdots & \ddots & \vdots\\ + y_{m1} & y_{m2} & \cdots & y_{mp} + \end{bmatrix} + = + \begin{bmatrix} + a_{11} & a_{12} & \cdots & a_{1n}\\ + a_{21} & a_{22} & \cdots & a_{2n}\\ + \vdots & \vdots & \ddots & \vdots\\ + a_{m1} & a_{m2} & \cdots & a_{mn} + \end{bmatrix} + \times + \begin{bmatrix} + b_{11} & b_{12} & \cdots & b_{1p}\\ + b_{21} & b_{22} & \cdots & b_{2p}\\ + \vdots & \vdots & \ddots & \vdots\\ + b_{n1} & b_{n2} & \cdots & b_{np} + \end{bmatrix} + + + \begin{bmatrix} + c_{11} & c_{12} & \cdots & c_{1p}\\ + c_{21} & c_{22} & \cdots & c_{2p}\\ + \vdots & \vdots & \ddots & \vdots\\ + c_{m1} & c_{m2} & \cdots & c_{mp} + \end{bmatrix} +$$ +$$ + y_{ij}= a_{i1} b_{1j} + a_{i2} b_{2j} +\cdots+ a_{in} b_{nj} + c_{ij} = \sum_{k=1}^n a_{ik}b_{kj} + c_{ij} +$$ + +Where +- $y$ is the output matrix, +- $a$ is the first input matrix, +- $b$ is the second input matrix, +- $c$ is the bias input matrix, +- $m$ the first input matrix number of rows, +- $n$ the first input matrix number of columns and second input matrix number of rows, +- $p$ the second input matrix number of columns + +#### Inputs and outputs + +##### `A` + +Tensor `A` is the first input tensor. + +The shape of tensor `A` is $(m \times n)$. + +###### Constraints + +- (C1) Number of spatial axes of tensor `A` + - Statement: The number of spatial axes of tensor `X` is 2. `[R1]` + - Rationale: This restriction is intoduced to simplify the implementation considering the actual industrial use cases. + +##### `B` + +Tensor `B` is the second input tensor. + +The shape of tensor `B` is $(n \times p)$. + +##### `C` + +Tensor `C` is the bias input tensor. + +The shape of tensor `C` is $(m \times p)$. + +##### `Y` + +Tensor `Y` is the output tensor. + +The shape of tensor `Y` is $(m \times p)$. + + + +### Formal specification + +The formal specification of the `gemm` operator using the Why3 language is provided below. This specification ensures the consistency and desired behavior of the operator within the constraints described. + +```ocaml +(** + Specification of Gemm operation on tensors with real numbers. ========TBC NOT SURE OF THE CORRECT IMPL IN WHY3 ====== + *) +module GemmReal + use int.Int + use map.Map + use tensor.Shape + use tensor.Tensor + use real.Real + (** Define the matrix multiplication function *) + let function matmul (A : tensor real) (B : tensor real) (i : int) (j : int) : real = + requires { A.shape[1] = B.shape[0] } (** Ensure matrix multiplication dimensions are consistent *) + ensures { result = sum(k: 0 to A.shape[1]-1) (A.value[i,A.shape[1]-i+k] * B.value[B.shape[0]-k+j,j]) } + { + sum(k: 0 to A.shape[1]-1) (A.value[i,k] * B.value[k,j]) + } + (** Define the gemm operation on tensors **) + let function gemm_tensor (A : tensor real) (B : tensor real) (C : tensor real) : tensor real = + requires { A.shape[1] = B.shape[0] /\ A.shape[0] = C.shape[0] /\ B.shape[1] = C.shape[1] } + ensures { forall i j. result.value[i,j] = matmul(A, B, i, j) + C.value[i,j] } + { + shape = [A.shape[0], B.shape[1]] ; + value = fun i j -> matmul(A, B, i, j) + C.value[i,j] ; + } +end +``` diff --git a/safety-related-profile/sonnx/ops/spec/informal/gemm/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/gemm/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu.md b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu.md new file mode 100644 index 00000000..2d5b07e3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu.md @@ -0,0 +1,232 @@ +# Contents + +- **LeakyRelu** operator for type [real](#real) +- **LeakyRelu** operator for types [float16, float, double](#float) + +Based on ONNX documentation version 14. + + +# **LeakyRelu** (real) + +## Signature +$Y = \text{LeakyRelu}(X)$ + +where: +- $X$: input tensor +- $Y$: result of the element-wise application of **LeakyRelu** on $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Attribute alpha must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | + + +## Informal specification + +Operator **LeakyRelu** is defined as follows: + +$$ +Y[i] = +\begin{cases} +X[i] & \text{if } X[i] \ge 0 \\ +\alpha \cdot X[i] & \text{if } X[i] < 0 \\ +\end{cases} +$$ + + + +### Example 1 + +```math +X = \begin{bmatrix} 6.1 & -9.5 & 35.7 \end{bmatrix} \\ +\alpha=0.1 +``` + +```math +Y \approx \text{LeakyRelu}(X) = \begin{bmatrix} 6.1 & -0.95 & 35.7 \end{bmatrix} +``` + +## Error conditions +No error condition. + +## Attributes + +### $\text{alpha}$: real +Coefficient of leakage. + +#### Constraints + +No constraint. + +## Inputs + +### $\text{X}$: real tensor +Argument of the **LeakyRelu**. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +## Outputs + +### $\text{Y}$: real tensor + +Tensor $Y$ is the output of the **LeakyRelu** applied to $X$. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Attributes + +Operator **LeakyRelu** has no attribute. + +## Formal specification + +See the Why3 specification. + + +# **LeakyRelu** (float) +where float is in {float16, float, double} + +## Signature +$Y = \text{LeakyRelu}(X)$ + +where: +- $X$: input tensor +- $Y$: result of the element-wise application of **LeakyRelu** on $X$ + +[General restrictions](./../common/general_restrictions.md) are applicable. + + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Attribute alpha must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | + +## Informal specification + +Operator **LeakyRelu** is defined as follows: + +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i] \in [\text{-Inf}, -0[~~ \wedge ~\alpha = \text{NaN} \\ +\alpha\cdot X[i] & \text{if } X[i] \in [\text{-Inf}, -0[~~ \wedge ~\alpha \neq \text{NaN} \\ +X[i] & \text{otherwise} \\ +\end{cases} +$$ + +### Example 1 + +```math + \alpha = 0.01 +``` + +```math +X = \begin{bmatrix} 6.1 & -9.5 & 35.7 \end{bmatrix} +``` + + +```math +Y = \text{LeakyRelu}(X) \approx \begin{bmatrix} 6.1 & -0.95 & 35.7 \end{bmatrix} +``` + +### Example 2 + +```math + \alpha = 0.01 +``` + +```math +X = \begin{bmatrix} + \text{inf} & \text{NaN} & \text{-inf} & -0.0 & 0.0 & 1.0 & -1.0 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + \text{inf} & \text{NaN} & \text{-inf} & \text{-0.0} & \text{0.0} & \text{1.0} & \text{-0.01} +\end{bmatrix} +``` + +### Example 3 + +```math + \alpha = \text{Nan} +``` + +```math +X = \begin{bmatrix} + \text{inf} & \text{NaN} & \text{-inf} & -0.0 & 0.0 & 1.0 & -1.0 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + \text{inf} & \text{NaN} & \text{Nan} & \text{-0.0} & \text{0.0} & \text{1.0} & \text{Nan} +\end{bmatrix} +``` + +### Example 4 + +```math + \alpha = \text{-inf} +``` + +```math +X = \begin{bmatrix} + \text{inf} & \text{NaN} & \text{-inf} & -0.0 & 0.0 & 1.0 & -1.0 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + \text{inf} & \text{NaN} & \text{inf} & \text{-0.0} & \text{0.0} & \text{1.0} & \text{inf} +\end{bmatrix} +``` + + + +## Error conditions +The function returns $\text{NaN}$ only when the input is $\text{NaN}$, or when the input is negative and alpha is $\text{NaN}$. + +## Inputs + +### $\text{X}$: floating-point tensor +Argument of the **LeakyRelu**. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +## Outputs + +### $\text{Y}$: floating-point tensor + +Tensor $Y$ is the output of the **LeakyRelu** applied to $X$. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Attributes + +Operator **LeakyRelu** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numeric accuracy + +[See the numeric accuracy note](./leakyrelu_acc.md). + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu_acc.md b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu_acc.md new file mode 100644 index 00000000..4ead84cb --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/leakyrelu_acc.md @@ -0,0 +1,75 @@ +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **LeakyRelu** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $(Y = \text{LeakyRelu}(X))$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of LeakyRelu: + +- $if (X[i] > 0): (\dfrac{dY}{dX} = 1)$ +- $if (X[i] < 0): (\dfrac{dY}{dX} = \alpha)$ +- $if (X[i] = 0)$: derivative is undefined, but LeakyRelu is Lipschitz continuous with constant $(\max(1, |\alpha|))$ + +A first-order bound is: + +- For every index (I) such that (X[I] > 0) and (X[I] + X_{\textit{err}}[I] > 0) (no sign crossing through 0): + + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |X_{\textit{err}}[I]|$ + +- For every index (I) such that (X[I] < 0) and (X[I] + X_{\textit{err}}[I] < 0) (no sign crossing through 0): + + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |\alpha| \cdot |X_{\textit{err}}[I]|$ + +- If (X[I]) and (X[I] + X_{\textit{err}}[I]) have different signs (crossing 0), the propagated error may be bounded using the Lipschitz constant: + + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le \max(1,|\alpha|) \cdot |X_{\textit{err}}[I]|$ + +### Error Introduction + +Error introduction for real (ideal) arithmetic is null: + +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto result = [&X](auto I) { + // Real-domain leakyrelu + return leakyrelu(X[I]); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + + auto y = result(I); + + // First-order propagated error bound + double bound; + if (x.real > 0 && x.real + x.err > 0) { + // Positive region: derivative = 1 + bound = std::abs(x.err); + } else if (x.real < 0 && x.real + x.err < 0) { + // Negative region: derivative = alpha + bound = std::abs(x.alpha * x.err); + } else { + // Crossing zero: use Lipschitz constant max(1, |alpha|) + bound = std::max(1.0, std::abs(x.alpha)) * std::abs(x.err); + } + + assert(std::abs(y.err) <= bound + 1e-12); +} +``` diff --git a/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/reviews/float_cases.png b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/reviews/float_cases.png new file mode 100644 index 00000000..ac7f74d8 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/leakyrelu/reviews/float_cases.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/less/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/less/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/less/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/less/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/less/less.md b/safety-related-profile/sonnx/ops/spec/informal/less/less.md new file mode 100644 index 00000000..ea8c9a37 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/less/less.md @@ -0,0 +1,389 @@ +# Contents + +- **Less** operator for type [real](#real) +- **Less** operator for types [float16, float, double](#float) +- **Less** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Less version 13](https://onnx.ai/onnx/operators/onnx__Less.html). + + +# **Less** (real, real) + +## Signature + +$C = \textbf{Less}(A, B)$ + +where +- $A$: real input tensor to compare +- $B$: real input tensor to compare with $A$ +- $C$: boolean result tensor of the element-wise comparison of $A$ and $B$ +- +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Less** operator. + +## Function +[E_ABS_REAL_LESS_010]
+Operator **Less** compares two input tensors $A$ and $B$ element-wise. +For each element, if the corresponding entry in $A$ is strictly less than the corresponding entry in $B$, the corresponding entry in $C$ contains the value $\text{True}$, otherwise it contains the value $\text{False}$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = +\begin{cases} +\text{True} & \text{if } A[i] < B[i] \\ +\text{False} & \text{otherwise} +\end{cases} +$$ +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +A = \begin{bmatrix} 2.0 & 3.0 & 7.0 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3.0 & 3.0 & 5.0 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} \text{True} & \text{False} & \text{False} \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 1.1 & 2.0 \\ 4.2 & 0.0 \\ 5.3 & 6.4 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3.5 & 2.0 \\ 4.6 & 1.0 \\ 5.7 & 4.8 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} +\text{True} & \text{False} \\ +\text{True} & \text{True} \\ +\text{True} & \text{False} +\end{bmatrix} +``` + +## Error conditions +No error condition. + +## Attributes + +Operator **Less** has no attribute. + +## Inputs + +### $\text{A}$: real tensor + +First input tensor to be compared. + +#### Constraints + +- `[E_LESS_REAL_CONSTR_A_010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +### $\text{B}$: real tensor + +Second input tensor to be compared with $A$. + +#### Constraints + + - `[E_LESS_REAL_CONSTR_B_010]` Shape consistency + - Statement: See constraint [E_LESS_REAL_CONSTR_A_010](#E_LESS_REAL_CONSTR_A_010) on tensor $A$. + +## Outputs + +### $\text{C}$: bool tensor + +Output tensor formed by the element-wise comparison of $A$ and $B$. + +#### Constraints + +- `[E_LESS_REAL_CONSTR_C_010]` Shape consistency + - Statement: See constraint [E_LESS_REAL_CONSTR_A_010](#E_LESS_REAL_CONSTR_A_010) on tensor $A$. + + + +# **Less** (float, float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Less}$ signature: +$C = \textbf{Less}(A, B)$ + +where +- $A$: float input tensor to compare +- $B$: float input tensor to compare with $A$ +- $C$: boolean result tensor of the element-wise comparison of $A$ and $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Less** operator. + +## Function +[E_ABS_FLOAT_LESS_010]
+Operator **Less** compares two input tensors $A$ and $B$ element-wise according to IEEE 754 floating-point semantics. +For each element, if the corresponding entry in $A$ is strictly less than the corresponding entry in $B$, the resulting tensor $C$ contains the value $\text{True}$. Otherwise, the resulting tensor $C$ contains the value $\text{False}$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = +\begin{cases} +\text{False} & \text{if } A[i]=\text{NaN} & or & B[i]=\text{NaN} \\ +\text{False} & \text{if } A[i]=\text{-0.0} & and & B[i]=\text{0.0} \\ +\text{False} & \text{if } A[i]=\text{0.0} & and & B[i]=\text{-0.0} \\ + +\text{True} & \text{if } A[i] < B[i] \\ +\text{False} & \text{otherwise} + +\end{cases} +$$ + +[END]
+ +Note: Comparisons involving NaN follow IEEE 754 rules (e.g., $\text{NaN} < x$ or $x < \text{NaN}$ is False for any $x$). + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +A = \begin{bmatrix} 2.5 & 3.7 & 7.9 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3.1 & 3.7 & 5.8 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} \text{True} & \text{False} & \text{False} \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 1.1 & 2.0 \\ 4.2 & 0.0 \\ 5.3 & 6.4 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3.5 & 2.0 \\ 4.6 & 1.0 \\ 5.7 & 4.8 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} +\text{True} & \text{False} \\ +\text{True} & \text{True} \\ +\text{True} & \text{False} +\end{bmatrix} +``` + +### Example 3 + +```math +A = \begin{bmatrix} \text{-inf} & \text{-inf} & \text{-inf} & \text{-inf} & 0.0 & 0.0 & 0.0 & 0.0 & \text{+inf} & \text{+inf} & \text{+inf} & \text{+inf} & \text{NaN} & \text{NaN} & \text{NaN} & \text{NaN} \end{bmatrix} +``` + +```math +B = \begin{bmatrix} \text{-inf} & 0.0 & \text{+inf} & \text{NaN} & \text{-inf} & 0.0 & \text{+inf} & \text{NaN} & \text{-inf} & 0.0 & \text{+inf} & \text{NaN} & \text{-inf} & 0.0 & \text{+inf} & \text{NaN} \end{bmatrix} +``` + +```math +C = \begin{bmatrix} \text{False} & \text{True} & \text{True} &\text{False} &\text{False} &\text{False} & \text{True} &\text{False} &\text{False} &\text{False} &\text{False} &\text{False} &\text{False} &\text{False} & \text{False} & \text{False} \end{bmatrix} +``` + + + +## Error conditions + +No error condition. + +## Attributes + +Operator **Less** has no attribute. + +## Inputs + +### $\text{A}$: floating-point tensor + +First input tensor to be compared. + +#### Constraints + +- `[E_LESS_FLOAT_CONSTR_A_010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +- `[E_LESS_FLOAT_CONSTR_A_020]` Type consistency + - Statement: Tensors $A$ and $B$ shall have the same floating-point type. + +### $\text{B}$: floating-point tensor + +Second input tensor to be compared with $A$. + +#### Constraints +- `[E_LESS_FLOAT_CONSTR_B_010]` Shape consistency + - Statement: see constraint [E_LESS_FLOAT_CONSTR_A_010](#E_LESS_FLOAT_CONSTR_A_010) on tensor $A$. +- `[E_LESS_FLOAT_CONSTR_B_020]` Type consistency + - Statement: see constraint [E_LESS_FLOAT_CONSTR_A_020](#E_LESS_FLOAT_CONSTR_A_020) on tensor $A$. + +## Outputs + +### $\text{C}$: bool tensor + +Output tensor formed by the element-wise comparison of $A$ and $B$. + +#### Constraints + +- `[E_LESS_FLOAT_CONSTR_C_010]` Shape consistency + - Statement: See constraint [E_LESS_FLOAT_CONSTR_A_010](#E_LESS_FLOAT_CONSTR_A_010) on tensor $A$. + +## Formal specification + See Why3 specification. + + + + +# **Less** (int,int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature + +Definition of operator $\text{Less}$ signature: +$C = \textbf{Less}(A, B)$ + +where +- $A$: int input tensor to compare +- $B$: int input tensor to compare with $A$ +- $C$: boolean result tensor of the element-wise comparison of $A$ and $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Less** operator. + +## Informal specification +[E_LESS_INT_FUNC_010]
+Operator **Less** compares two input tensors $A$ and $B$ element-wise using integer comparison. +For each element, if the corresponding entry in $A$ is strictly less than the corresponding entry in $B$, the resulting tensor $C$ contains the value $\text{True}$. Otherwise, the resulting tensor $C$ contains the value $\text{False}$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = +\begin{cases} +\text{True} & \text{if } A[i] < B[i] \\ +\text{False} & \text{otherwise} +\end{cases} +$$ + +[END]
+ +The examples given in the real section apply directly when restricted to integer values. +### Example 1 + +```math +A = \begin{bmatrix} 2 & 3 & 7 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3 & 3 & 5 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} \text{True} & \text{False} & \text{False} \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 1 & 2 \\ 4 & 0 \\ 5 & 6 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 3 & 2 \\ 4 & 1 \\ 5 & 4 \end{bmatrix} +``` + +Result $C$ is: + +```math +C = \begin{bmatrix} +\text{True} & \text{False} \\ +\text{False} & \text{True} \\ +\text{False} & \text{False} +\end{bmatrix} +``` + + +## Error conditions + +No error condition. + +## Attributes + +Operator **Less** has no attribute. + +## Inputs + +### $\text{A}$: integer tensor + +First input tensor to be compared. + +#### Constraints + +- `[E_LESS_INT_CONSTR_A_010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. +- `[E_LESS_INT_CONSTR_A_020]` Type consistency + - Statement: Tensors $A$ and $B$ shall have the same integer type. + +### $\text{B}$: integer tensor + +Second input tensor to be compared with $A$. + +#### Constraints + +- `[E_LESS_INT_CONSTR_B_010]` Shape consistency + - Statement: See constraint [`E_LESS_INT_CONSTR_A_010`](#E_LESS_INT_CONSTR_A_010) on tensor $A$. +- `[E_LESS_INT_CONSTR_B_020]` Type consistency + - Statement: See constraint [`E_LESS_INT_CONSTR_A_020`](#E_LESS_INT_CONSTR_A_020) on tensor $A$. + +## Outputs + +### $\text{C}$: bool tensor + +Output tensor formed by the element-wise comparison of $A$ and $B$. + +#### Constraints + +- `[E_LESS_INT_CONSTR_C_010]` Shape consistency + - Statement: See constraint [E_LESS_INT_CONSTR_A_010](#E_LESS_INT_CONSTR_A_010) on tensor $A$. + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/less/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/less/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/log/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/log/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/log/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/log/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/log/log.md b/safety-related-profile/sonnx/ops/spec/informal/log/log.md new file mode 100644 index 00000000..f678abd4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/log/log.md @@ -0,0 +1,224 @@ + +# Contents + +- **Log** operator for type [real](#real) +- **Log** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Log version 13](https://onnx.ai/onnx/operators/onnx__Log.html). + + +# **Log** (real) + +## Signature +$Y = \textbf{Log}(X)$ + +where: +- $X$: Input tensor +- $Y$: Natural logarithm of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Log** operator, besides domain constraints explicitly stated below. + +## Informal specification + +The **Log** operator computes the element-wise natural logarithm of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +**Log** is only defined for strictly positive values. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = \log(X[i]) +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 1 & 2 & 4 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0 & 0.693147 & 1.386294 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + 2.718 & 7.389 \\ + 0.01 & 0.1 \\ + 10 & 1000 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.999896 & 1.999992 \\ + -4.605170 & -2.302585 \\ + 2.302585 & 6.907755 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Log** has no attribute. + +## Inputs + +### $\text{X}$: real tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Definition domain + - Statement: $\forall i,\ X[i] > 0$. + +## Outputs + +### $\text{Y}$: real tensor + +Natural logarithm of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1rx). + + + +# **Log** (float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Log}$ signature: +$Y = \textbf{Log}(X)$ + +where: +- $X$: Input tensor +- $Y$: Natural logarithm of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Log** operator. + +## Informal specification + +The **Log** operator computes the element-wise natural logarithm of the input tensor $X$ according to IEEE 754 floating-point semantics. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + + +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i]=\text{NaN} \\ +\text{NaN} & \text{if } X[i] \in [\text{-inf}, \text{-0}[ \\ +\text{-inf} & \text{if } X[i] = 0 \\ +\text{inf} & \text{if } X[i] = \text{inf} \\ +\log(X[i]) & \text{otherwise} \\ + +\end{cases} +$$ + + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 1 & 2 & 4 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0 & 0.69314718 & 1.38629436 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + 2.718 & -7.389 \\ + 0.0 & 0.1 \\ + 10 & -1000 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.99989629 & \text{NaN} \\ + \text{-inf} & -2.30258512 \\ + 2.30258512 & \text{NaN} +\end{bmatrix} +``` + + +### Example 3 + +```math +X = \begin{bmatrix} + \text{inf} & \text{NaN} & \text{-inf} & -0.0 & 0.0 +\end{bmatrix} +``` + +```math +Y = \begin{bmatrix} + \text{inf} & \text{NaN} & \text{NaN} & \text{-inf} & \text{-inf} +\end{bmatrix} +``` + +## Error conditions + +The function returns $\text{NaN}$ when the input is strictly negative and different from -0.0. + +## Attributes + +Operator **Log** has no attribute. + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: $X$ and $Y$ shall have the same floating-point type. + +## Outputs + +### $\text{Y}$: floating-point tensor + +Natural logarithm of tensor $X$ (with IEEE 754 handling of zero and negative inputs). + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1fx). +- `[C2]` Type consistency + - Statement: See [constraint (C2) on X](#C2fx). + +## Numeric accuracy + +[See the numeric accuracy note](./log_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/log/log_acc.md b/safety-related-profile/sonnx/ops/spec/informal/log/log_acc.md new file mode 100644 index 00000000..7fa11240 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/log/log_acc.md @@ -0,0 +1,55 @@ +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Log** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = \log(X)$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\log$ is $d\log(x)/dx = 1/x$, a first-order bound is: + +- For every index $I$ such that $X[I] > 0$ and $X[I] + X_{\textit{err}}[I] > 0$ (no crossing of the singularity at 0): + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le \left|\frac{X_{\textit{err}}[I]}{X[I]}\right|$ + + +- If $X[I]$ and $X[I] + X_{\textit{err}}[I]$ have different signs or approach zero too closely, the bound may become very large (logarithm near its singularity at 0). + +### Error Introduction +Error introduction for real (ideal) arithmetic is null: +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto result = [&X](auto I) { + // Real-domain log, undefined for non-positive inputs + return (X[I].real > 0) ? log(X[I]) + : SymbolicDomainError::undef(); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + + // Ensure we stay in the domain of log under perturbation + if (x.real > 0 && x.real + x.err > 0) { + auto y = result(I); + + // First-order propagated error bound: |err_log| <= |err_x / x| + double bound = std::abs(x.err / x.real); + + assert(std::abs(y.err) <= bound + 1e-12); + } +} +``` \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/log/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/log/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/Bidirectional-LSTM.png b/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/Bidirectional-LSTM.png new file mode 100644 index 00000000..c5ad051c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/Bidirectional-LSTM.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/LSTM_Cell.png b/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/LSTM_Cell.png new file mode 100644 index 00000000..3926fd30 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/lstm/assets/imgs/LSTM_Cell.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/lstm.md b/safety-related-profile/sonnx/ops/spec/informal/lstm/lstm.md new file mode 100644 index 00000000..b77f8255 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/lstm/lstm.md @@ -0,0 +1,303 @@ + +# Contents +- `LSTM` [operator (real)](#real) +- `LSTM` [operator (FP16, FP32, FP64, BFLOAT16)](#float) + +Operator `LSTM` computes the output of an architecture including one or several Long Term Short Term Memory cells. Cells are organized in a number of layers equal to the length of the input sequences. The organization can be forward, reverse or bi-directional. + +The following figure presents the use of a LSTM cell in a bidirectional architecture presenting three layers. + +![](./figures/Bidirectional-LSTM.png) + +LSTM Cell internal diagram for input, memory and output gates controlled by sigmoïds and flow activated by hyperbolic tangent. + +![](./figures/LSTM_Cell.png) + + +### Notations + +These constants are used to define tensor shapes: +- `seq_length`: Number of layers in the archirecture. The same LSTM cell, with the same parameters is repeated at each layer of the architecture for each direction. I.e. when there are two directions there are only two sets of parameters. For forward architectures each layer corresponds to a time step of the simulation of the dynamic behavior of a single LSTM cell with delayed feedbacks of the state cell $c_{t-1} \gets c_t$ and the hidden layer $h_{t-1} \gets h_t$. +- `batch_size`: size of the batch. +- `input_size`: number of feature of the input tensor. +- `num_directions` : 1 if forward or reverse LSTM, 2 if bidirectional LSTM. + +$\odot$ identifies the Hadamard product, i.e. element wise multiplication. + + +# `LSTM` operator (real) + +### Signature +`Y = LSTM(X,W,R,B,sequence_lens,initial_h,initial_c,P)` +where +- `Y`: the output tensor +- `X`: the input tensor +- `W`: the weight tensor +- `R`: the recurrence weight tensor +- `B`: the bias tensor +- `sequence_lens`: the lengths of the sequences in a batch +- `initial_h`: the value of hidden vector h at $t_0$ +- `initial_c`: the value of the cell tensor c at $t_0$ +- `P`: the weight tensor for peepholes. + + +#### Inputs and outputs + +##### `X` + +Tensor `X` is the input tensor. +The shape of tensor `X` is $(seq\textunderscore length \times batch\textunderscore size \times input\textunderscore size)$. + +##### `W` + +Tensor `W` is the weight input tensor. + +The shape of tensor `W` is $(num\textunderscore directions \times 4*hidden\textunderscore size \times input\textunderscore size)$. + +$$ + W = + \begin{bmatrix} + W_{i} \\ + W_{o} \\ + W_{f} \\ + W_{g} \\ + \end{bmatrix} +$$ + +##### `R` + +Tensor `R` is the recurrence weight input tensor. + +The shape of tensor `R` is $(num\textunderscore directions \times 4*hidden\textunderscore size \times hidden\textunderscore size)$. + +$$ + R = + \begin{bmatrix} + R_{i} \\ + R_{o} \\ + R_{f} \\ + R_{g} \\ + \end{bmatrix} +$$ + +##### `B` + +Tensor `B` is the bias input tensor. + +The shape of tensor `B` is $(num\textunderscore directions \times 8*hidden\textunderscore size)$. + +$$ + B = + \begin{bmatrix} + B_{wi} \\ + B_{wo} \\ + B_{wf} \\ + B_{wg} \\ + B_{ri} \\ + B_{ro} \\ + B_{rf} \\ + B_{rg} + \end{bmatrix} +$$ + +##### `sequence_lens`: + +Optional tensor specifying lengths of the sequences in a batch. If not specified - assumed all sequences in the batch to have length `seq_length`. It has shape [`batch_size`]. + +##### `initial_h`: + +Optional initial value of the hidden state. If not specified - assumed to be 0. It has shape [`num_directions`, `batch_size`, `hidden_size`]. + +##### `initial_c`: + +Optional initial value of the cell. If not specified - assumed to be 0. It has shape [`num_directions`, `batch_size`, `hidden_size`]. + +##### `P` + +The weight tensor for peepholes. Concatenation of P[iof] and PB[iof] (if bidirectional) along dimension 0. It has shape [`num_directions`, 3*`hidden_size`]. Optional: If not specified - assumed to be 0. + +##### `Y` + +Tensor `Y` is the output tensor. + +The shape of tensor `Y` is $(seq\textunderscore length \times num\textunderscore directions \times batch\textunderscore size \times hidden\textunderscore size)$. + +#### Attributes + +##### `activation_alpha` + +Optional scaling values used by some activation functions. The values are consumed in the order of activation functions, for example (f, g, h) in LSTM. Default values are the same as of corresponding ONNX operators.For example with LeakyRelu, the default alpha is 0.01. + +##### `activation_beta` + +Optional scaling values used by some activation functions. The values are consumed in the order of activation functions, for example (f, g, h) in LSTM. Default values are the same as of corresponding ONNX operators. + +##### `activations` + +The value is a string of 3 comma separated values 'act1, act2, act3' where act_i $\in$ {`Relu`, `Tanh`, `Sigmoid`} + +Defaults to 'Sigmoid, Tanh, Tanh'. + +if `direction` is `bidirectional`, the value is a string of 6 comma separated values. +The first 3 values corresponding to the forward layer, the 3 last values correponding to the reverse layer. + +Defaults to 'Sigmoid, Tanh, Tanh, Sigmoid, Tanh, Tanh'. + +#### `clip` + +Cell `clip` threshold. Clipping bounds the elements of a tensor in the range of [-threshold, +threshold] and is applied to the input of activations. No `clip` if not specified. + +##### `direction` + +Specify if the RNN is forward, reverse, or bidirectional. Must be one of `forward` (default), `reverse`, or `bidirectional`. + +##### `hidden_size` + +Number of neurons in the hidden layer. Shall be set to the hyper-parameter `hidden_size` + +##### `input_forget` + +Couple the input and forget gates if 1. +$$ + c_t = (1 - act1(i_t)) \odot c_{t-1} + act1(i_t) \odot act2(g_t) +$$ + +and $W_f$, $R_f$, $B_{wf}$ and $B_{rf}$ not relevent. + +##### `layout` + +The shape format of inputs X, initial_h, initial_c and outputs Y, Y_h, Y_c. If 0, the following shapes are expected: X.shape = [seq_length, batch_size, input_size], Y.shape = [seq_length, num_directions, batch_size, hidden_size], initial_h.shape = Y_h.shape = initial_c.shape = Y_c.shape = [num_directions, batch_size, hidden_size]. If 1, the following shapes are expected: X.shape = [batch_size, seq_length, input_size], Y.shape = [batch_size, seq_length, num_directions, hidden_size], initial_h.shape = Y_h.shape = initial_c.shape = Y_c.shape = [batch_size, num_directions, hidden_size]. + +### Informal specification + +The algorithm of the LSTM Cell is the following: +``` +Y = LSTM(X,W,R,B,sequence_lens,initial_h,initial_c,P){ + if direction is bidirectional + Y_for = LSTM_Forward(X, ...) + Y_rev = revert(LSTM_Forward(revert(X), ...)) + Y = concat(Y_for, Y_rev) + else if direction is forward + Y = LSTM_Forward (X, ...) + else if direction is reverse + Y = revert(LSTM_Forward(revert(X), ...)) +} + +revert(X) returns the reversed X tensor along `seq_length` axis. +concat(X1,X2) concatenates X1 and X2 tensor along `seq_length` axis. +``` + +#### Mathematical definition of LSTM_Forward + +$$ + \forall t \in [1, seq\textunderscore length], +$$ +$$ + h_0 = initial\textunderscore h +$$ +$$ + c_0 = initial\textunderscore c +$$ +$$ + x_t = X[t-1] +$$ +$$ + \begin{bmatrix} + i_t \\ + o_t \\ + f_t \\ + g_t + \end{bmatrix} + = + \begin{bmatrix} + W_{i} & R_{i} \\ + W_{o} & R_{o} \\ + W_{f} & R_{f} \\ + W_{g} & R_{g} + \end{bmatrix} + \times + \begin{bmatrix} + x_t \\ + h_{t-1} + \end{bmatrix} + + + \begin{bmatrix} + B_{wi} + B_{ri} \\ + B_{wo} + B_{ro} \\ + B_{wf} + B_{rf} \\ + B_{wg} + B_{rg} + \end{bmatrix} +$$ +$$ + c_t = act1(f_t) \odot c_{t-1} + act1(i_t) \odot act2(g_t) +$$ +$$ + h_t = act1(o_t) \odot act3(c_t) +$$ +$$ + Y[t-1] = h_t +$$ + +For peephole architecture the equations are the following: + +$$ +\begin{bmatrix} + i_t \\ + o_t \\ + f_t \\ + g_t + \end{bmatrix} + = + \begin{bmatrix} + W_{i} & R_{i} & 0 & P_{i} \\ + W_{o} & R_{o} & P_{o} & 0 \\ + W_{f} & R_{f} & 0 & P_{f}\\ + W_{g} & R_{g} & 0 & 0 + \end{bmatrix} + \times + \begin{bmatrix} + x_t \\ + h_{t-1} \\ + c_t \\ + c_{t-1} + \end{bmatrix} + + + \begin{bmatrix} + B_{wi} + B_{ri} \\ + B_{wo} + B_{ro} \\ + B_{wf} + B_{rf} \\ + B_{wg} + B_{rg} + \end{bmatrix} +$$ + +$P_i$, $P_o$ and $P_f$ are diagonal matrices. + + +Where +- $i$ is the input gate matrix, +- $o$ is the output gate matrix, +- $f$ is the forget gate matrix, +- $g$ is the cell input gate matrix, +- $c$ is the cell state, +- $h$ is the hidden state, + + +# `LSTM` operator (FP16, FP32, FP64, BFLOAT16) + +#### Accuracy + +Activation functions required accuracy is 1 ULP. + +### SONNX restrictions + +The following input and attributes shall be explicitely defined: +- `W` and `R` shall be set to constant tensors. +- `initial_h`, `initial_c` shall be set to constant tensor or to a 0 tensor if not used. +- `B` shall be set to a constant tensor or a 0 tensor if not used. +- `batch_size` shall be set to 1 when batch is not supported. +- `sequence_lens` shall be set to a constant tensor or to `batch_size` if not used. +- `P` shall be set to a constant tensor or 0 tensor if not used. +- `input_forget` shall be set to 0 if not used. +- `layout` shall be set to 0 if not used. +- `activation` is restricted to 'Sigmoid, Tanh, Tanh' or 'Relu, Tanh, Tanh' + diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/eric.md b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/eric.md new file mode 100644 index 00000000..8d7c3823 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/eric.md @@ -0,0 +1,51 @@ +- Hyper-parameters + - Are they "parameters" or "notations". It seems to me that they are *** notations *** used to designate "attributes" of the input tensors. Can we really call them "hyper-parameters"?. + - nu_directions is a specific case since is is actuall "computed" by the algotithm. I would not introduce it... + + - Suppress "defines the" : + > `seq_length`: number of time steps for the LSTM cell + - foward => forward + +- Signature + +- Informal specification + - Check title level + - "Operator LSTM computes the Long Term Short Term Memory Cell forward, backward, or bidirectional." => "Operator LSTM computes the forward, backward, or bidirectional Long Term Short Term Memory Cell." + - Shouldn't we add (somewhere) some reference to the academic paper? + - "is described as follows" => "is the following"? + - Where does the "direction" "parameter" come from? + - I don't understand the role of "num_directions". is it an input or some output? + - shouldn't we avoid the use of "..." in the specification? + - Formula to be reformated. + - The activation functions "act1" and "act2" and "act3" are not defined. Should they be attributes? + - Are you sure about Y[t-1]=h_t? (just check) +- Inputs and outputs + - "is intoduced" => "is introduced" + - "considering the actual industrial use case" : discuss how to formulate this... + - About the tensor sizes, we should probably clarifies / explain the notation (in particular "*" stands for usual multiplication whereas "\times" introduces a new tensor dimension. + - "sequence_lens [...) T1" => "sequence_lens [...) T" + - Note that as we are in the real domain, we should probably replace "T" by "real"? => to be discussed + - "It has shape batch size" => "Th shape of tensor `sequence_lens` is (batch_size). (to be corrected everywhere) + - "optional initial value of the hidden" => "optional initial value of the hidden state" + - "assumed to be ..." => is it really "assumed to be xxxx"? => "if not specified the initial value of h is set to 0" => to be discussed. + - check the font for all notations (sometime `xxx`and sometimes xxx) +- Attributes + - We should not see "floats". + - activation_alpha : not necessarily a "scaling" value. For a Leaky ReLu, for instance, it corresponds to the leakiness factor. + - "The value is a coma separated $3 \times STRINGS$ where 'act1, act2, act3' values can be taken in {`Relu`, `Tanh`, `Sigmoid`}." + - => "The value is a string of 3 comma separated values 'A1,A2,A3' where the $Ai \in {`Relu`, `Tanh`, `Sigmoid`}$. + - "Defaults to" => "if not specified..." + - be careful : "coma" => "comma" (with two "m"s. + - if "direction is bidirectional" + => add the first case "if direction is not bidirectional" then... + - "hiden_size" + - I don't understand the sentence "shall be set to hidden_size". If it is an attribute, then it is the value provided by the user. There is no need to add this "hyper-parameter"... + - input_forget + - shouldn't this attribute paly a role in the algorithm? + - layout + - to be discussed. Seems to refer to Python implementation... + - Explicit inputs and attributes + - We ahev called them "restrictions" => SONNX restrictions, to be discussed + + + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/jean-loup.md b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/jean-loup.md new file mode 100644 index 00000000..22df0eb0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/jean-loup.md @@ -0,0 +1,59 @@ +"Operator LSTM computes forward, reverse, or bidirectional Long Term Short Term Memory Cell." <- "Operator LSTM computes the output of an architecture including one or several Long Term Short Term Memory cells. Cells are organized in a number of layers equal to the length of the input sequences. The organization can be forward, reverse or bi-directional." + +"LSTM Bidirectional layer" <- "The following figure presents the use of a LSTM cell in a bidirectional architecture presenting three layers. Note that the sum of outputs is performed outside of the architecture defined by the operator." + +"LSTM Cell internal diagram" <- "LSTM Cell internal diagram for input, memory and output gates controlled by sigmoïds and flow activated by hyperbolic tangent" + +"seq_length" : Number of layers in the archirecture. The same LSTM cell, with the same parameters is repeated at each layer of the architecture for each direction. I.e. when there are two directions there are only two sets of parameters. For forward architectures each layer corresponds to a time step of the simulation of the dynamic behavior of a single LSTM cell with delayed feedbacks of the state cell $c_{t-1} \gets c_t$ and the hidden layer $h_{t-1} \gets h_t$. + +I don't understand the presence of "batch_size" in an inference context. Make the constraint "batch_size = 1" unconditional in SONNX restrictions? + +"hidden_size" also defines tensor shapes. + +I don't understand "for example (f, g, h) in LSTM" . I know that's in the ONNX documentation but "for exemple in LSTM" makes no sense because we are specifying LSTM. I think f, g, h are respectively what is called latter act1, act2 and act3. I think we should write, "act1, act2 and act3 as defined in Section Mathematical definition of LSTM_Forward". + +"clip" should be in bold characters. + +"input_forget" : -> + +$$ + c_t = (1 - act1(i_t)) \odot c_{t-1} + act1(i_t) \odot act2(g_t) +$$ + +and $W_f$, $R_f$, $B_{wf}$ and $B_{rf}$ not relevent. + +It is strange that for "direction is reverse" there is no "revert", i.e. "Y = revert( LSTM_Forward (revert(X),...))" + +If peepholes are authorized we should write: + +$$ +\begin{bmatrix} + i_t \\ + o_t \\ + f_t \\ + g_t + \end{bmatrix} + = + \begin{bmatrix} + W_{i} & R_{i} & 0 & P_{i} \\ + W_{o} & R_{o} & P_{o} & 0 \\ + W_{f} & R_{f} & 0 & P_{f}\\ + W_{g} & R_{g} & 0 & 0 + \end{bmatrix} + \times + \begin{bmatrix} + x_t \\ + h_{t-1} \\ + c_t \\ + c_{t-1} + \end{bmatrix} + + + \begin{bmatrix} + B_{wi} + B_{ri} \\ + B_{wo} + B_{ro} \\ + B_{wf} + B_{rf} \\ + B_{wg} + B_{rg} + \end{bmatrix} +$$ + +Indicating that $P_i$, $P_o$ and $P_f$ are diagonal matrices. diff --git a/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/meeting_mom.md b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/meeting_mom.md new file mode 100644 index 00000000..9f5e234c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/lstm/reviews/meeting_mom.md @@ -0,0 +1,27 @@ +# Meeting 03-07-2025 10:00 + +Attendees: nicolas.valot@airbus.com, Sebastian.Boblest@de.bosch.com, Valentin.Poirot@de.bosch.com + +1. To specify that seq_length, batch_size, input_size shall be constant values (linked to tensor dimensions) + +2. We should not be restrictive so that the profile will raise some interest in the ONNX community. +I will remove restrictions on initial conditions, peephole. +We agreed that sequence_lens might be used only when batch_size is > 1, but appears to be exotic. +B (bias) is not optional and shall be defined to 0..0 when unused to respect the 'unambiguous / explicit' requirements. + +3. This raises the need of : +a tool to convert ONNX to SONNX (to convert implicit attributes to explicit ones, to add reshape upon broadcast ...) +a tool to verify SONNX profile (extension of the existing ONNX checker ?) +Comments: +Bosh says it is urgent to propose and commit with ONNX that the SONNX profile will become mainstream. +To push into this direction, we need a clever way to specify the operators, so that our specification complies with ONNX without any restriction. + +I propose that for each operator .md file, we specify the full compliance with ONNX spec and we add a bottom section SONNX in the operator page, where we express the restrictions like : this input XXX shall be static, the B bias shall be explicitly defined, Broadcast not supported (reshape shall be prepended when required)... + +This will show our specification work (math expressions, illustrations) mainstream to all the ONNX community from the ONNX documentation entry point, and also the SONNX 'value' for curious/interested people at the bottom of each operator page. + +For visibility and adoption, we shall avoid having a SONNX documentation entry point different from mainstream ONNX. + +I will try to reorganize the lstm.md file in this manner to see what it looks like. + +We shall trace our modifications in the lstm/review folder (to be created). \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/matmul/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/matmul/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/matmul/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/matmul/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul.md b/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul.md new file mode 100644 index 00000000..9f5bbb4a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul.md @@ -0,0 +1,479 @@ +# Contents +- **MatMul** operator for type [real](#real) +- **MatMul** operator for types [int32, int64, uint32, uint64](#int) +- **MatMul** operator for types [float16, float32, float64](#float) + +Based on ONNX [Op version 13](https://onnx.ai/onnx/operators/onnx__MatMul.html). + + +# **MatMul** (real, real) + +# Signature +$Y = \textbf{MatMul}(A, B)$ + +where: +- $A$: left-side of the multiplication +- $B$: right-side of the multiplication +- $Y$: result of the multiplication + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **MatMul** operator for the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Input tensors $A$ and $B$ have 2 dimensions | Transient | + +## Informal specification + +Operator **MatMul** computes a matrix multiplication. + +The mathematical definition of the operator is given hereafter. + +$$ + \forall i \in [0, dA_0 - 1], \forall j \in [0, dB_1 - 1] \quad Y[i,j] = \sum_{k=0}^{dA_1-1} A[i,k]\times B[k,j] +$$ + +with $dA_1 = dB_0$ + +### Example 1 + +$$ + A = \begin{bmatrix} + 1 & 2 \\ + 3 & 4 + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 5 & 6 \\ + 7 & 8 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + 19 & 22 \\ + 43 & 50 +\end{bmatrix} +$$ + +### Example 2 +$$ +A = \begin{bmatrix} + 1 & 2 \\ + 3 & 4 \\ + 5 & 6 + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 7 & 8 & 9 \\ + 10 & 11 & 12 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + 27 & 30 & 33 \\ + 61 & 68 & 75 \\ + 95 & 106 & 117 +\end{bmatrix} +$$ + +## Error conditions + +No error conditions. + +## Attributes + +Operator **MatMul** has no attributes. + +## Inputs + +### $A$: real + +Tensor $A$ is the left-side input tensor. + +#### Constraints + +- `E_MATMUL_REAL_CONSTR_A_0010` Number of spatial axes of tensor $A$ + - Statement: The number of spatial axes of tensor $A$ is 2. + + +- `E_MATMUL_REAL_CONSTR_A_0020` Consistency of $A$ and $B$ shapes + - Statement: $dA_1=dB_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + + +- `E_MATMUL_REAL_CONSTR_A_0030` Consistency of $A$ and $Y$ shapes + - Statement: $dY_0=dA_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +### $B$: real + +Tensor $B$ is the right-side input tensor. + +#### Constraints + +- `E_MATMUL_REAL_CONSTR_B_0010` Number of spatial axes of tensor $B$ + - Statement: The number of spatial axes of tensor $B$ is 2. + + +- `E_MATMUL_REAL_CONSTR_B_0020` Consistency of $A$ and $B$ shapes + - See constraint [E_MATMUL_REAL_CONSTR_A_0020](#E_MATMUL_REAL_CONSTR_A_0020) of A. + + +- `E_MATMUL_REAL_CONSTR_B_0030` Consistency of $B$ and $Y$ shapes + - Statement: $dY_1=dB_1$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +## Outputs + +### $Y$: real + +Tensor $Y$ is the output tensor. + +#### Constraints + +- `E_MATMUL_REAL_CONSTR_Y_0010` Number of spatial axes of tensor $Y$ + - Statement: The number of spatial axes of tensor $Y$ is 2. + + +- `E_MATMUL_REAL_CONSTR_Y_0020` Consistency of $A$, $B$ and $Y$ shapes + - See constraint [E_MATMUL_REAL_CONSTR_A_0030](#E_MATMUL_REAL_CONSTR_A_0030) of A and constraint [E_MATMUL_REAL_CONSTR_B_0030](#E_MATMUL_REAL_CONSTR_B_0030) of B + + + + +# **MatMul** (float, float) + +where float is in {float16, float32, float64} + +# Signature +$Y = \textbf{MatMul}(A, B)$ +where +- $A$: left-side input tensor +- $B$: right-side input tensor +- $Y$: output tensor + + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) are applicable. + + +## Informal specification + +Operator **MatMul** computes the tensor operation equivalent to a matrix multiplication. + +The mathematical definition of the operator is given hereafter for a bidimensional tensors. + +$$ + Y = AB +$$ + + +$$ + \begin{bmatrix} + Y_{00} & Y_{01} & \cdots & Y_{0c_B}\\ + Y_{10} & Y_{11} & \cdots & Y_{1c_B}\\ + \vdots & \vdots & \ddots & \vdots\\ + Y_{l_A0} & Y_{l_A1} & \cdots & Y_{l_Ac_B} + \end{bmatrix} + = + \begin{bmatrix} + A_{00} & A_{01} & \cdots & A_{0c_A}\\ + A_{10} & A_{11} & \cdots & A_{1c_A}\\ + \vdots & \vdots & \ddots & \vdots\\ + A_{l_A0} & A_{l_A1} & \cdots & A_{l_Ac_A} + \end{bmatrix} + \begin{bmatrix} + B_{00} & B_{01} & \cdots & B_{0c_B}\\ + B_{10} & B_{11} & \cdots & B_{1c_B}\\ + \vdots & \vdots & \ddots & \vdots\\ + B_{l_B0} & B_{l_B1} & \cdots & B_{l_Bc_B} + \end{bmatrix} +$$ + +$$ + \forall i \in [0, l_A], \forall j \in [0, c_B] \quad Y_{ij} = \sum_{k=0}^{n-1} A_{ik}B_{kj} +$$ + +Where +- $l_A = dA_0 - 1$ +- $c_A = dA_1 - 1$ +- $l_B = dB_0 - 1$ +- $c_B = dB_1 - 1$ +- $n = dA_1 = dB_0$ + +### Example 1 +$$ +A = \begin{bmatrix} + 1.0 & 2.0 \\ + 3.0 & 4.0 + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 5.0 & 6.0 \\ + 7.0 & 8.0 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + 19.0 & 22.0 \\ + 43.0 & 50.0 +\end{bmatrix} +$$ + +### Example 2 +$$ +A = \begin{bmatrix} + \infty & -\infty & NaN \\ + NaN & \infty & -\infty \\ + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 1.0 & 2.0 \\ + 4.0 & 5.0 \\ + 7.0 & 8.0 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + NaN & NaN \\ + NaN & NaN +\end{bmatrix} +$$ + +### Example 3 + +$$ +A = \begin{bmatrix} + \infty & \infty \\ + NaN & \infty \\ + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 1.0 & 2.0 & 3.0 & 4.0 \\ + 4.0 & 5.0 & 6.0 & 7.0 \\ + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + \infty & \infty & \infty & \infty \\ + NaN & NaN & NaN & NaN \\ + \end{bmatrix} +$$ + +## Error conditions + +No error conditions. + +## Attributes + +Operator **MatMul** has no attributes. + +## Inputs + +### $\text{A}$: float + +Tensor $A$ is the left-side input tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $A$ + - Statement: The number of spatial axes of tensor $A$ is 2. + +- `[C2]` Consistency of $A$ and $B$ shapes + - Statement: $dA_1=dB_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +- `[C3]` Consistency of $A$ and $Y$ shapes + - Statement: $dY_0=dA_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +### $\text{B}$: float + +Tensor $B$ is the right-side input tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $B$ + - Statement: The number of spatial axes of tensor $B$ is 2. + +- `[C2]` Consistency of $A$ and $B$ shapes + - [See constraint (C2) of A](#C2f) + +- `[C3]` Consistency of $B$ and $Y$ shapes + - Statement: $dY_1=dB_1$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +## Outputs + +### $\text{Y}$: float + +Tensor $Y$ is the output tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $Y$ + - Statement: The number of spatial axes of tensor $Y$ is 2. + +- `[C2]` Consistency of $A$, $B$ and $Y$ shapes + - [See constraint (C3) of A](#C3f) and [constraint (C3) of B](#C4f) + + +# **MatMul** (int, int) + +where int is in {int32, int64, uint32, uint64} + +# Signature +$Y = \textbf{MatMul}(A, B)$ +where +- $A$: left-side of the multiplication +- $B$: right-side of the multiplication +- $Y$: result of the multiplication + + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **MatMul** operator for the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | Input tensors $A$ and $B$ have 2 dimensions | Transient | + +## Informal specification + +Operator **MatMul** computes a matrix multiplication. + +The mathematical definition of the operator is given hereafter. + +$$ + \forall i \in [0, dA_0 - 1], \forall j \in [0, dB_1 - 1] \quad Y[i,j] = \sum_{k=0}^{dA_1-1} A[i,k]\times B[k,j] +$$ + +with $dA_1 = dB_0$ + +where +- $x+y$ = [**Add**](../add/add.md#add-int-int)$(x,y)$ +- $x\times y$ = [**Mul**](../mul/mul.md#mul-int-int)$(x,y)$ + +### Example 1 +$$ +A = \begin{bmatrix} + 1 & 2 \\ + 3 & 4 + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 5 & 6 \\ + 7 & 8 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + 19 & 22 \\ + 43 & 50 +\end{bmatrix} +$$ + +### Example 2 +$$ +A = \begin{bmatrix} + 1 & 2 \\ + 3 & 4 \\ + 5 & 6 + \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} + 7 & 8 & 9 \\ + 10 & 11 & 12 + \end{bmatrix} +$$ + +$$ +Y = \begin{bmatrix} + 27 & 30 & 33 \\ + 61 & 68 & 75 \\ + 95 & 106 & 117 +\end{bmatrix} +$$ + +## Error conditions + +No error conditions. + +## Attributes + +Operator **MatMul** has no attributes. + +## Inputs + +### $\text{A}$: itype + +Tensor $A$ is the left-side input tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $A$ + - Statement: The number of spatial axes of tensor $A$ is 2. + +- `[C2]` Consistency of $A$ and $B$ shapes + - Statement: $dA_1=dB_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +- `[C3]` Consistency of $A$ and $Y$ shapes + - Statement: $dY_0=dA_0$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +### $\text{B}$: itype + +Tensor $B$ is the right-side input tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $B$ + - Statement: The number of spatial axes of tensor $B$ is 2. + +- `[C2]` Consistency of $A$ and $B$ shapes + - [See constraint (C2) of A](#C2i) + +- `[C3]` Consistency of $B$ and $Y$ shapes + - Statement: $dY_1=dB_1$ + - Rationale: Application of the mathematical definition of matrix multiplication. + +## Outputs + +### $\text{Y}$: itype + +Tensor $Y$ is the output tensor. + +#### Constraints + +- `[C1]` Number of spatial axes of tensor $Y$ + - Statement: The number of spatial axes of tensor $Y$ is 2. + +- `[C2]` Consistency of $A$, $B$ and $Y$ shapes + - [See constraint (C3) of A](#C3i) and [constraint (C3) of B](#C4i) + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul_acc.md b/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul_acc.md new file mode 100644 index 00000000..f904fb02 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/matmul/matmul_acc.md @@ -0,0 +1,95 @@ +# Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +## Note Algorithm + +We retain the mathematical definition of the operator for a 2D tensor + +$$ + Y = A \times B +$$ + +$$ + \begin{bmatrix} + Y[0, 0] & Y[0, 1] & \cdots & Y[0, p-1]\\ + Y[1, 0] & Y[1, 1] & \cdots & Y[1, p-1]\\ + \vdots & \vdots & \ddots & \vdots\\ + Y[m-1, 0] & Y[m-1, 1] & \cdots & Y[m-1, p-1] + \end{bmatrix} + = + \begin{bmatrix} + A[0, 0] & A[0, 1] & \cdots & A[0, n-1]\\ + A[1, 0] & A[1, 1] & \cdots & A[1, n-1]\\ + \vdots & \vdots & \ddots & \vdots\\ + A[m-1, 0] & A[m-1, 1] & \cdots & A[m-1, n-1] + \end{bmatrix} + \times + \begin{bmatrix} + B[0, 0] & B[0, 1] & \cdots & B[0, p-1]\\ + B[1, 0] & B[1, 1] & \cdots & B[1, p-1]\\ + \vdots & \vdots & \ddots & \vdots\\ + B[n-1, 0] & B[n-1, 1] & \cdots & B[n-1, p-1] + \end{bmatrix} +$$ +$$ + Y[i, j]= A[i, 0]\times B[0, j] + A[i, 1]\times B[1, j] +\cdots+ A[i, n-1]\times B[n-1, j] = \sum_{k=0}^{n-1} A[i, k]\times B[k, j] +$$ + +Where + +- $m$ is the number of rows of matrix $A$ (= $dA[0]$) +- $n$ the number of columns of matrix $A$ (= $dA[1]$) and the number of rows of matrix B (= $dA[0]$), +- $p$ is the number of columns of matrix $B$ (= $dB[1]$) + +Let us define $|A| = \max_{0 \leq i < m, 0 \leq j < n} | A[i, j] |$, +$|B| = \max_{0 \leq i < n, 0 \leq j < p} | B[i, j] |$, +$|A_{\textit{err}}| = \max_{0 \leq i < m, 0 \leq j < n} | A_{\textit{err}}[i, j] |$ +and $|B_{\textit{err}}| = \max_{0 \leq i < n, 0 \leq j < p} | B_{\textit{err}}[i, j] |$. + +## Error Propagation - for information - see [guidelines](../../../docs/guidelines/accuracy.md#error-propagation) + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, +where $Y$ is the tensor result of the **Matmul** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). +For $Y = A \times B$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from +the input errors $A_{\textit{err}}[i, j]$ and $B_{\textit{err}}[i, j]$. + +Using the derivative of Matmul, a first-order bound is: + +- For every index $i, j$: + - $|Y_{\textit{err}}^{\textit{propag}}[i, j]| = \left(\sum_{k=0}^{n-1} A_{\textit{err}}[i, k]\times B[k, j]\right) + \left(\sum_{k=0}^{n-1} A[i, k]\times B_{\textit{err}}[k, j]\right) +\mathcal{O}(|A_{\textit{err}}|\times |B_{\textit{err}}|)$ + +- The complete definition of $\mathcal{O}(|A_{\textit{err}}|\times |B_{\textit{err}}|)$ + is available in the [guidelines](../../../docs/guidelines/accuracy.md#error-propagation). +- A global bound for the propagated error is: + - $|Y_{\textit{err}}^{\textit{propag}}[i, j]| \le n\times (|A_{\textit{err}}||B| + |A||B_{\textit{err}}|) + n\times (|A_{\textit{err}}||B_{\textit{err}}|)$ + +This operator amplifies an initial error by absolute value of the coefficients of the other matrix. + +## Error Introduction (real) + +Error introduction for real (ideal) arithmetic is null: + +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +## Error Introduction (IEEE-754 floating-point) + +Let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\textbf{u}} = \frac{\varepsilon}{2}$. + +We suppose a naïve implementation for the computation of $\sum_{k=0}^{n-1} A[i, k]\times B[k, j]$ +to establish the formula above. Every implementation could improve the accuracy by reordering +the terms of the sum and/or with more accurate local additions and multiplications. + +Hence, for the standard rounding mode round to nearest even, provided $Y_{\textit{val}}[i, j]$ are +normal numbers + +$$|Y_{\textit{err}}^{\textit{intro}}[i, j]| \leq \left((1+\textit{\textbf{u}}^2) \times \frac{(1 + \textit{\textbf{u}})^n - 1}{\textit{\textbf{u}}} - n\right) \times (|A| + |A_{\textit{err}}|) \times (|B| + |B_{\textit{err}}|) $$ + +This formula is obtained with a proof by induction over $n$. + +## Unit Verification + +To be defined + diff --git a/safety-related-profile/sonnx/ops/spec/informal/matmul/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/matmul/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/max/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/max/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/max/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/max/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/max/max.md b/safety-related-profile/sonnx/ops/spec/informal/max/max.md new file mode 100644 index 00000000..edc18f59 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/max/max.md @@ -0,0 +1,200 @@ +# Contents +- **Max** operator for type [real](#real) +- **Max** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) +- **Max** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Max version 13](https://onnx.ai/onnx/operators/onnx__Max.html). + + +# **Max** (real, ..., real) + +## Signature +$Y = \text{Max}(X0, ... , XL)$ + +where: +- $X0$, ... , $XL$ input tensors with L $\in [0, 2^{31}-1[$ +- $Y$: result of the element-wise maximum among $X0$, ... , $XL$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Max** operator. + +## Informal specification + +Operator **Max** is applied on $\dot X0$,... , $\dot XL$ where $\dot X0$,..., $\dot XL$ is the broadcasted form of $X0$,..., $XL$, +i.e. ($\dot X0$, ... , $\dot XL$) = Broadcast($X0$, ... , $XL$), cf. [broadcast](./../common/broadcast/broadcast.md). + +Thanks to broadcasting, all $\dot Xm$ for $m \in [0, L]$ have the same shape. + +The maximum is taken element-wise among the elements of the different broadcasted tensors presenting identical indexes. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$Y[i] = \max_{m \in [0, L] } \dot Xm[i]$$ + +The maximum shall comply with the mathematical definition of the function denoted $\max$. + +## Error conditions +No error conditions for **Max**. + +## Inputs + +### $\text{X0,...,XL}$: real tensors +Tensors among which the maximum is to be taken element-wise after broadcasting. +#### Constraints + +- `[C1]` Broadcasting + - Statement: Tensors $X0$,..., $XL$ shall be broadcastable. + +- `[C2]` Shape consistency + - Statement: Tensors $\dot Xm$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: real tensor + +Tensor $Y$ is the element-wise result of the maximum among broadcasted $X0$,..., $XL$. + +#### Constraints + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C2ra) on tensors $\dot Xm$. + +## Attributes + +Operator $\text{Max}$ has no attribute. + + + +# **Max** (float, ..., float) +where float is in {float16, float, double} + +## Signature +$Y = \text{Max}(X0, ... , XL)$ + +where: +- $X0$, ... , $XL$ input tensors with L $\in [0, 2^{31}-1[$ +- $Y$: result of the element-wise maximum among $X0$, ... , $XL$ after broadcasting + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Max** operator. + + + ## Informal specification + +Operator **Max** is applied on $\dot X0$,... , $\dot XL$ where $\dot X0$,..., $\dot XL$ is the broadcasted form of $X0$,..., $XL$, +i.e. ($\dot X0$, ... , $\dot XL$) = Broadcast($X0$, ... , $XL$), cf. [broadcast](./../common/broadcast/broadcast.md). + +Thanks to broadcasting, all $\dot Xm$ for $m \in [0, L]$ have the same shape. + +The maximum is taken element-wise among the elements of the different broadcasted tensors presenting identical indexes. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$Y[i] = \max_{m \in [0, L] } \dot Xm[i]$$ + +The maximum shall comply with the mathematical definition of the function denoted $\max$. + +Note that the types considered here have special values that do not inherit naturally the order defined on the real numbers (>) underlying the maximum function, i.e. Inf, 0, 0-, NaN. For those values the following order shall be assumed when considering the maximum function: + +Inf > any positive number > 0 > -0 > any negative number > -Inf. + +NaN is an absorbing element for **Max**. + +Note: In the ONNX runtime implementation, when 0 and -0 are compared, **Max** returns the first value : **Max**(0,-0) = 0 and **Max**(-0,0)=0-. + +## Error conditions +No error conditions for **Max**. + +## Inputs + +### $\text{X0,...,XL}$: float tensors +Tensors among which the maximum is to be taken element-wise. + +#### Constraints +- `[C1]` Broadcasting + - Statement: Tensors $X0$,..., $XL$ shall be broadcastable. + +- `[C2]` Shape consistency + - Statement: Tensors $\dot Xm$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: float tensor + +Tensor $Y$ is the element-wise result of the maximum among among broadcasted $X0$,..., $XL$. + +#### Constraints + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C2fa) on tensors $\dot Xm$. + +## Attributes + +Operator $\text{Max}$ has no attribute. + + +# **Max** (int, ..., int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature +$Y = \text{Max}(X0, ... , XL)$ + +where: +- $X0$, ... , $XL$ input tensors with L $\in [0, 2^{31}-1[$ +- $Y$: result of the element-wise maximum among $X0$, ... , $XL$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Max** operator. + +## Informal specification + +Operator **Max** is applied on $\dot X0$,... , $\dot XL$ where $\dot X0$,..., $\dot XL$ is the broadcasted form of $X0$,..., $XL$, +i.e. ($\dot X0$, ... , $\dot XL$) = Broadcast($X0$, ... , $XL$), cf. [broadcast](./../common/broadcast/broadcast.md). + +Thanks to broadcasting, all $\dot Xm$ for $m \in [0, L]$ have the same shape. + +The maximum is taken element-wise among the elements of the different broadcasted tensors presenting identical indexes. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$Y[i] = \max_{m \in [0, L] } \dot Xm[i]$$ + +The maximum shall comply with the mathematical definition of the function denoted $\max$. + +## Error conditions +No error conditions for **Max**. + +## Inputs + +### $\text{X0,...,XL}$: int tensors +Tensors among which the maximum is to be taken element-wise after broadcasting. +#### Constraints + +- `[C1]` Broadcasting + - Statement: Tensors $X0$,..., $XL$ shall be broadcastable. + +- `[C2]` Shape consistency + - Statement: Tensors $\dot Xm$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: int tensor + +Tensor $Y$ is the element-wise result of the maximum among broadcasted $X0$,..., $XL$. + +#### Constraints + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C2ia) on tensors $\dot Xm$. + +## Attributes + +Operator $\text{Max}$ has no attribute. + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/max/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/max/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/max/reviews/max-ejn.md b/safety-related-profile/sonnx/ops/spec/informal/max/reviews/max-ejn.md new file mode 100644 index 00000000..e8385a61 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/max/reviews/max-ejn.md @@ -0,0 +1,131 @@ +> The format has slightly changed. Please refer to [div](../../div/div.md) for an up to date example; + + +# `max` operator +### Contents +- `Maximum` operator for a type on which an order is defined. +## `Max` `(type on which an order is defined, i.e. bfloat16, double, float, float16, int16, int32, int64, int8, uint16, uint32, uint64, uint8)` + +### Signature +`Y = max(X^1, ... , X^N)` +where + +- `N`: +- `X^1`: first input tensor +- ... +- `X^N`: last input tensor +- `Y`: output tensor + + +> Variadic arguments are noted with a subscript $A_i$. + +#### Restrictions +The following restrictions apply to the `max` operator for the SONNX profile: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `R1` | `N` is an integer between 1 and 2147483647 | Transient | +| `R2` | Numpy boardcasting rules shall be applicable to `Y`, `X^1`, ... , `X^N` | https://numpy.org/doc/stable/user/basics.broadcasting.html | + +> I think that the restriction on the number of variadic parameters is not a restriction but a constraint. + + #### Informal specification + +> Everywhere: replace "boardcasted" by "broadcasted" ;-) + +The result tensor $Y$ is based on the boardcasted values $Z^1$, ... , $Z^N$ of the input tensors $X^1$, ... , $X^N$. + +> The meaning of "is based" must be clarified. We could define a broadcast operator $\text{broadcast}$ and define operator max applied on $Z_1, Z_2,...$ where $Z_i=\text{broadcast}(X_i,...)$. Stated differently: $\text{max}(X_1,X_2,...)= \text{max}(\text{broadcast}(X_1), \text{broadcast}(X_2),...)$ and define the $\text{broadcast}$ operator elsewhere. + +> In the informal specification of the $\text{broadcast}$ operator, introduce first the opeator in an informal way: +> Boroadcasting is the operation consisting in expanding the dimensions of a tensor to make its shape compatible the shape of the other arguments in an element-wise operation (e.g., $\text{add}$, $\text{mul}$, etc.). + +$ Please use the notation $dT_i$ for the it-th dimension of tensor $T$. + +$ Please use the notion of + +Let, $I$, $J$, $K$, $L$... be the common boardcasted dimensions of all tensors, elements $y_{i,j,k,l...}$ of $Y$ shall comply with: + +> (Genera) Always give an informal explanation before an equation . + +> Here is an alternate formulation... +> +> Let $Y=\text{broadcast}(X_1,X_2,...,X_n)$ where $Y$ is the broadcasted value of tensor $X1$ with respect to t$X2$, $X3$?... +> +> The broadcast operator is associative, ie. +> $$Y=\text{broadcast}(...\text{broadcast}(\text{broadcast}(\text{broadcast}(X_1,X_2),X_3),...,Xn)$$ + + +> When we compute $A+B$ with broadcasting, we actually compute $b(A,B)+b(B,A)$ + +> Additionaly operator $b$ is commutative in terms of shape: +> $$\text{shape}(b(A,B)) = \text{shape}(b(B,A))$$ + + +> So we can just define $Y=\text{broadcast}(X1,X2)$, + +> The following constraints are applicable: + +> (C1) Dimension must be compatible: +> $$\forall i \in [0,n-1], dX1_i = dX2_i \vee dX1_i = 1 \vee dX2_i = 1$$ +> (C2) Dimension of the broadcasted tensor: +> $$\forall i \in [1,n], dY_i = \max(dX_{1,i}, dX_{2,i})$$ + +> Note 1: I have to state that if a tensor has only $n$ dimensions , then $\forall i>n, dX_i=1$. + +> Note 2: that I refer to the max operation here.$\text{max}$ is the $\text{max}$ applied on scalar. We could define broadcast as a special case in order to prevent an infinitely recursive definition or simply say that this $\text{max}$ is the "usual" max. + +> Now, I would define the values of $Y$ from the values of $X1$ and $X2$. + +> If tensor T has $nT$ dimensions, then +> $$\forall i_1,i_2,...,i_n \in [0,dY_1-1]\times[0,dY_2-1]\times [0,dY_n-1] : Y(i_1,i_2,...i_n) = \text{op}(X1(i_1,i_2,i_{nX1}), X2(i_1,i_2,i_{nX2}))$$ + + + +$\forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... ~~~~ y_{i,j,k,l...} = \max_{n \in [1, N] } z^n_{i,j,k,l...}$ + +> $z^n_{i,j,k,l...}$ => $z_n(i,j,k,l...)$ + +where $z^n_{i,j,k,l...}$ is an element of $Z^n$. + +> I don't understand why the common dimension of the broadcasted tensor would depend on the values of the tensors. I a probably missing the point. + + +##### Numpy boardcasting +$I$, $J$, $K$, $L$... are reciprocaly defined as $I = \max_{n \in [1, N] } I_n$, $J = \max_{n \in [1, N] } J_n$, $K = \max_{n \in [1, N] } K_n$, $L = \max_{n \in [1, N] } L_n$... where $I_n$, $J_n$, $K_n$, $L_n$... are the dimensions of the $n$ th input tensor. + +The following restrictions apply to the Numpy boardcasting: + +| Restriction | Statement | Origin | +| -------- | ------- | ------- | +| `RI` | $\forall n \in [1, N]$ either $I_n = I$ or $I_n = 1$| https://numpy.org/doc/stable/user/basics.broadcasting.html | +| `RJ` | $\forall n \in [1, N]$ either $J_n = J$ or $J_n = 1$| https://numpy.org/doc/stable/user/basics.broadcasting.html | +| `RK` | $\forall n \in [1, N]$ either $K_n = K$ or $K_n = 1$| https://numpy.org/doc/stable/user/basics.broadcasting.html | +| `RL` | $\forall n \in [1, N]$ either $L_n = L$ or $L_n = 1$| https://numpy.org/doc/stable/user/basics.broadcasting.html | +| ... | ... | ... | + + +Assuming those restrictions hold, the relation between elements of boardcasted tensors and input tensors are: + +$\forall n \in [1, N], \forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... z^n_{i,j,k,l...} = x^n_{f(i,I_n,I),f(j,J_n,J),f(k,K_n,K),f(l,L_n,L)...}$ + +Where $f(.,.,.)$ is a function such that: + +$f(a,B,C) = a$ if $B=C$ and $f(a,B,C) = 1$ if $B=1$. + +Note that other cases, i.e. $B \neq C$ and $B \neq 1$, don't need to be specified because of restrictions `RI`, `RJ`, `RK`, `RL`... + +#### Properties +From the definition of the maximum we have two properties: + +$\forall n \in [1, N], \forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... ~~~~ y_{i,j,k,l...} \geq z^n_{i,j,k,l...}$ + +$\forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... \exists n \in [1, N] ~~| ~~~~ y_{i,j,k,l...} = z^n_{i,j,k,l...}$ + +The same properties written in function of unboardcasted inputs are: + +$\forall n \in [1, N], \forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... ~~~~ y_{i,j,k,l...} \geq x^n_{f(i,I_n,\max_{m \in [1, N] } I_m),f(j,J_n,\max_{m \in [1, N] } J_m),f(k,K_n,\max_{m \in [1, N] } K_m),f(l,L_n,\max_{m \in [1, N] } L_m)...}$ + +$\forall i \in [1, I], \forall j \in [1, J], \forall k \in [1, K], \forall l \in [1, L]... \exists n \in [1, N] ~~| ~~~~ y_{i,j,k,l...} = x^n_{f(i,I_n,\max_{m \in [1, N] } I_m),f(j,J_n,\max_{m \in [1, N] } J_m),f(k,K_n,\max_{m \in [1, N] } K_m),f(l,L_n,\max_{m \in [1, N] } L_m)...}$ + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/drawio/onnx_maxpool.drawio b/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/drawio/onnx_maxpool.drawio new file mode 100755 index 00000000..1be123b5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/drawio/onnx_maxpool.drawio @@ -0,0 +1,6242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/imgs/MaxPool_with_dilation.drawio.png b/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/imgs/MaxPool_with_dilation.drawio.png new file mode 100755 index 00000000..504ac481 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/maxpool/assets/imgs/MaxPool_with_dilation.drawio.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/maxpool.md b/safety-related-profile/sonnx/ops/spec/informal/maxpool/maxpool.md new file mode 100644 index 00000000..b23351d4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/maxpool/maxpool.md @@ -0,0 +1,1123 @@ +# Contents + +- **MaxPool** operator for type [real](#real) +- **MaxPool** operator for types [float16, float, double](#float) +- **MaxPool** operator for types [int8, uint8](#int) + +Based on ONNX [MaxPool version 22](https://onnx.ai/onnx/operators/onnx__MaxPool.html). + + +# **MaxPool** (real) + +## Signature +($Y, \textit{Indices}) = \textbf{MaxPool}(X)$ + +where: +- $X$: Input tensor +- $Y$: Output tensor containing max value selected from $X$ +- $\textit{Indices}$: Output tensor containing the indices in $X$ from where the max values are taken. + + +## Restrictions + +[General restrictions](../common/general_restrictions.md) are applicable. + +The following specific restrictions apply to the **MaxPool** operator: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input tensor $X$ has 2 spatial axes | Transient | +| `[R2]` | All attributes must be explicitly set | [No default values](../../../../../deliverables/reqs/reqs.md#no_default_value) +| `[R3]` | Attribute `auto_pad` is restricted to NOTSET | Transient +| `[R4]` | Attribute `ceil_mode` is set to zero | Transient +| `[R5]` | Attribute `storage_order` is set to zero | Transient + + +## Informal specification + +Operator **MaxPool** consumes an input tensor $X$ and applies max pooling across the tensor according to the kernel shape, strides, dilations and pads. Max pooling consists of computing the max on all values of a subset of the input tensor according to the kernel shape and downsampling the data into the output tensor $Y$. + +**MaxPool** is a sliding window operator like **Conv**, for instance. At a given position, the sliding window, called "kernel" or "$W$" in this document, is only there to select the set of elements of $X$ of which the maximum shall be computed. Therefore, only the shape of the kernel matters for **MaxPool**. + +Operator **MaxPool** stores in $\textit{Indices}$ the indices of the input tensor $X$ from which the max values are taken. The index values are those of a flatten 1-D view of $X$. + +In any position of the kernel over $X_p$, if the max value is present more than once, then the corresponding index value in $\textit{Indices}$ points the most upper left max value that belongs the input tensor X. + +The mathematical definition of output $Y$ and $\textit{Indices}$ are given hereafter: + +$$\begin{gathered} + Y[b, c, m, n] = \text{max}_{h=0}^{dW_0-1} \text{max}_{w=0}^{dW_1-1} \\ X_p[b,c,m \cdot \text{strides}[0]+ h \cdot \text{dilations}[0], n \cdot c+ w \cdot \text{dilations}[1] ] +\end{gathered}$$ + +$$\begin{gathered} + \textit{Indices}[b, c, m, n] = b \cdot (dX_1 \cdot dX_2 \cdot dX_3) + c \cdot (dX_2 \cdot dX_3) + h \cdot dX_3 + w +\end{gathered}$$ +$$\text{with:} \quad \text{Y[b, c, m, n] = X[h,w]}$$ + +__ALTERNATIVE__: + +$$\begin{gathered} + Y[b, c, h_{\text{out}}, w_{\text{out}}] = \max_{(j_h, j_w) \in \mathcal{V}} X[b, c, h_{\text{in}}, w_{\text{in}}] +\end{gathered}$$ + +Definition of $\mathcal{V}$: + +$$\begin{gathered} + \mathcal{V} = \{ (j_h, j_w) \mid 0 \le h_{\text{in}} < dX_2 \quad \text{and} \quad 0 \le w_{\text{in}} < dX_3 \} +\end{gathered}$$ + +Definition of $h_{\text{in}}$ and $w_{\text{in}}$: + +$$\begin{gathered} + h_{\text{in}} = h_{\text{out}} \cdot \text{strides}[0] + j_h \cdot \text{dilations}[0] - \text{x1\_begin}\end{gathered}$$ + +$$\begin{gathered} + w_{\text{in}} = w_{\text{out}} \cdot \text{dilations}[1] + j_W \cdot \text{dilations}[1] - x2\_begin +\end{gathered}$$ + +Where: $0 \le j_h < dW_0$ et $0 \le j_w < dW_1$. + + +$\textit{Indices}$: + +$$\begin{gathered} + (j_h^*, j_w^*) = \operatorname{argmax}_{(j_h, j_w) \in \mathcal{V}} X[n, c, h_{\text{out}} \cdot \text{strides}[0] + j_h \cdot {dilations}[0] - x1\_begin, w_{\text{out}} \cdot \text{strides}[1] + j_W \cdot \text{dilations}[1] - x2\_begin] +\end{gathered}$$ + +On calcule ensuite les coordonnées absolues correspondantes dans le tenseur d'entrée $X$ : +$$\begin{gathered} + h_{\text{in}}^* = h_{\text{out}} \cdot \text{strides}[0] + j_h^* \cdot {dilations}[0] - x1\_begin +\end{gathered}$$ + +$$\begin{gathered} + w_{\text{in}}^* = w_{\text{out}} \cdot \text{strides}[1] + j_w^* \cdot {dilations}[1] - x2\_begin +\end{gathered}$$ + +Finally: + +$$\begin{gathered} + \textit{Indices}[b, c, m, n] = b \cdot (dX_1 \cdot dX_2 \cdot dX_3) + c \cdot (dX_2 \cdot dX_3) + h_{\text{in}}^* \cdot dX_3 + w_{\text{in}}^* +\end{gathered}$$ + +Where +- $h_{\text{in}} \in [0,dX_2-1]$ is the index on the first spatial axis of $X_p$, whose dimension is $dX_2$. +- $w_{\text{in}} \in [0,dX_3-1]$ is the index on the second spatial axis of $X_p$, whose dimension is $dX_3$. +- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$ +- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$ +- $h_{\text{out}} \in [0,dY_2-1]$ is the index along the first spatial axis of output $Y$ +- $w_{\text{out}} \in [0,dY_3-1]$ is the index along the second spatial axis of output $Y$ +- $dW_0$ is the dimension of the first spatial axis of the kernel, i.e., the first value of attribute `kernel_shape` +- $dW_1$ is the dimension of the second spatial axis of the kernel, i.e., the second value of attribute `kernel_shape` +- `strides` is an attribute of the operator. It will be described later in this section. +- `dilation` is an attribute of the operator. It will be described later in this section. +- $X_{p} = \text{pad}(X,pads)$ is the padded version of the input tensor $X$. Function $\text{pad}$ applies padding as specified by attribute [pads](#real_pads). + +### A graphical view of MaxPool: + + + +### Example + +$\textit{Y},\textit{Indices} = \text{MaxPool}(X)$ + +- Shape of $X$ = [1, 1, 8, 8] +- kernel\_shape = [3,3] +- pads = [0,0,0,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 6, 6] +- Shape of $\textit{Indices}$ = [1, 1, 6, 6] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 5.67591154 & -0.04859958 & 2.94203104 & 3.70327292 & 2.47014306 & 4.12455586 & 5.81838665 & 1.84118807 \\ + -0.05267874 & 2.75227858 & 2.16608732 & 4.03416243 & 1.28184638 & 4 81748948 & 4.64878412 & 3.31626988 \\ + 3.55427648 & 0.39997585 & 4.45761508 & 4.82722666 & 0.18843372 & 0.49564314 & 7.96647029 & 4.82851447 \\ + 1.52417623 & 2.28965587 & 0.36251913 & 1.64413983 & 4.67267459 & 3.73167179 & 2.20052118 & 2.06720836 \\ + -1.22446366 & -0.86469519 & 6.01461967 & -1.08813165 & 2.11920055 & 0.78561867 & 0.29834533 & 1.94499626 \\ + 1.57776732 & 3.64260188 & 3.47181319 & 4.83723727 & 1.49868674 & 3.27683692 & 2.42625178 & 0.4401565 \\ + 6.8972704 & 5.51113868 & 5.99293336 & 4.24088721 & 1.94993561 & -0.04040625 & 3.07940675 & 3.06769141 \\ + 3.1299626 & 4.5546675 & 3.5008191 & 2.06181403 & 3.27400104 & 6.70386189 & 0.92777015 & -1.29092574 + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 5.67591154 & 4.82722666 & 4.82722666 & 4.82722666 & 7.96647029 & 7.96647029 \\ + 4.45761508 & 4.82722666 & 4.82722666 & 4.82722666 & 7.96647029 & 7.96647029 \\ + 6.01461967 & 6.01461967 & 6.01461967 & 4.82722666 & 7.96647029 & 7.96647029 \\ + 6.01461967 & 6.01461967 & 6.01461967 & 4.83723727 & 4.67267459 & 3.73167179 \\ + 6.8972704 & 6.01461967 & 6.01461967 & 4.83723727 & 3.27683692 & 3.27683692 \\ + 6.8972704 & 5.99293336 & 5.99293336 & 6.70386189 & 6.70386189 & 6.70386189 + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 19 & 19 & 19 & 22 & 22 \\ + 18 & 19 & 19 & 19 & 22 & 22 \\ + 34 & 34 & 34 & 19 & 22 & 22 \\ + 34 & 34 & 34 & 43 & 28 & 29 \\ + 48 & 34 & 34 & 43 & 45 & 45 \\ + 48 & 50 & 50 & 61 & 61 & 61 + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + + +## Error conditions +No error conditions. + + +## Attributes + +### `auto_pad`: string + +The `auto_pad` attribute determines if and how automatic padding is done for the input tensor X. + +#### Constraints +- `[C1]`: Value domain + - Statement: `auto_pad` shall be in set {NOTSET, VALID, SAME_UPPER, SAME_LOWER}. + - Rationale: [`[R2]`](#R2) +- `[C2]`: Explicit padding + - Statement: `auto_pad` shall be set to NOTSET. + - Rationale: [`[R3]`](#R3) + +[See constraint (C2) of Y](#shape_consist) + + +### `ceil_mode`: int + +Whether to use floor (0, default) or ceil (1) to compute the output shape. See the description of output $Y$. + +#### Constraints +- `[C1]`: Value domain + - Statement: `ceil_mode` shall be in set, i.e., to 0 (zero) or 1. + - Rationale: [`[R2]`](#R2) +- `[C2]`: floor mode is selected + - Statement: `ceil_mode` shall be set to 0. + - Rationale: [`[R4]`](#R4) + +### `dilations`: list of ints + +Dilation value along each spatial axis of filter. + +Attribute `dilations` specifies the spacing between the elements of $W$. The ith value in the list gives the dilation factor for spatial axis $i$. If the dilation factor is greater than 1 for axis $i$, then the kernel elements are spaced out by the dilation factor for that axis. + +The effect of the `dilations` attribute for a tensor with two spatial axes is depicted on the following figure. + + + +In the example above: +- `dilations`=(2,2) +- Before dilation, $W$ contains only 1s. Those 1s are used as selectors of values of $X$. +- After dilation, a '0' means that the value in $X$ is not selected. +- The offset between two '1' in the dilated $W$ along one spacial axis equals the dilation value for that axis, i.e., '2' in the example. Therefore, at a given position of $W$ on $X$, one value of $X$ over two is selected for computing the max along each spatial axis. + +#### Constraints +- `[C1]`: Value domain + - Statement: `dilations` is a list of strictly positive integers + - Rationale: The dilation is a *factor of expansion* along a certain axis. +- `[C2]`: Relation between `dilations`and $X$ + - Statement: the length of the `dilations` list is the number of spatial dimensions of $X$ ([see constraint `C1` of $X$](#C1ia)). + - Rationale: Dilation is defined for all axes of $X$. +- `[C3]`: Relation between `dilations` and $W$ + - Statement: The `dilations` and `kernel_shape` lists have the same length. + - Rationale: Dilation is defined for all axes of $W$. +- `[C4]`: Consistency between the shape of tensors $Y$, $X$ and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: [see constraint `C2` of $Y$](#shape_consist) + + +### `kernel_shape`: list of ints + +The size of the kernel along each spatial axis. + +#### Constraints +- `[C1]`: Value domain + - Statement: `kernel_shape` is a list of strictly positive integers + - Rationale: The max must be computed on at least one element. +- `[C2]`: Relation between `kernel_shape`and `pads` + - Statement: [see constraint `[C2]` of `pads`](#Keff_less_than_pads) +- `[C3]`: Relation between `kernel_shape` and `dilation` + - Statement: [see constraint `[C3]` of `dilation`](#dil_kernel_lists) +- `[C4]`: Consistency between the shape of tensors $Y$, $X$ and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: [see constraint `[C1]` and `[C2]` of $Y$](#shape_consist) + + +### `pads`: list of ints + +Attribute `pads` determines the padding at the beginning and end along each spatial axis of the input tensor $X$. + +`pads` is a list of the form (x1_begin, x2_begin,..., x1_end, x2_end,...), where xi_begin is the number of elements (possibly zero) added at the beginning of axis $i$ and xi_end is the number of elements added at the end of axis $i$. + +The value of the constant to pad depends on the input tensor data type. Therefore: +- see [floating-point value to pad](#pad_const_float_val) +- see [integer values to pad](#pad_const_int_val) + +The effect of the `pads` attribute is illustrated on the following figure on integers. In this example, `pads`=(2,1,2,2) and the padded value is the one for type uint8, i.e., zero. + +drawing + +#### Constraints +- `[C1]`: Consistency between the shape of $X$ and the length of `pads` + - Statement: The length of the `pads` list is twice the number of spatial axes of $X$ + - Rationale: Padding shall be given for all spatial axes, and a begining value and an end value must be given for each axis. +- `[C2]` : Consistency between padding and possibly dilated kernel + - Statement: Each dimension of the possibly dilated kernel shall be strictly greater than the greatest value in `pads` for the same dimension, i.e.: + + $Keff_i > \text{max}(pads[i], pads[i+2])$ + where $Keff_i = (\texttt{dilations}[i] * (\texttt{kernel\_shape}[i] - 1) + 1)$ + - Rationale: TBD. +- `[C3]`: Consistency between the shape of tensors $Y$, $X$ and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: [See constraint `[C1]` of Y](#shape_consist) + +### `storage_order`: int + +The storage order of the tensor. 0 is row major, and 1 is column major. + +#### Constraints +- `[C1]`: Explicit storage order + - Statement: `storage_order` shall be set to zero. + - Rationale: [`[R2]`](#R2), [`[R5]`](#R5) + +### `strides`: list of ints + +Attribute `strides` determines how the kernel is applied on tensor $X$ during the **MaxPool**. + +For instance, with $\texttt{stride}[0]=3$ and $\texttt{stride}[1]=2$, the kernel is applied to data 2 units on right in the first spatial axis and to data 3 units down in the second spatial axis at each step of the max pooling. + +The effect of the `strides` attribute is illustrated on the following figure. In this example, `strides`=(3,2). + + + +#### Constraints +- `[C1]`: Value domain + - Statement: `strides` is a list of strictly positive integers. + - Rationale: Stride values represent the number of applications of the kernel in the two spatial dimensions +- `[C2]`: Consistency between the shape of tensors $Y$, $X$ and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: [See constraint `[C1]` of Y](#shape_consist) + + + +## Inputs + +### $\text{X}$: real + +$X$ is the input tensor from which the max values are selected. + + +#### Constraints + +- `[C1]` First constraint on $X$ + - Statement: The number of spatial axes of tensor $X$ is 2. + - Rationale: [`[R1]`](#R1). +- `C2`: Consistency between the shape of tensors $Y$, $X$ and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: [See constraint `[C1]` of Y](#shape_consist) + + +## Outputs + +### $\text{Y}$: real + +The shape of the output $Y$ is $(dY_0 , dY_1 , dY_2 , dY_3)$ where +- $dY_0$ is the number of batches +- $dY_1$ is the number of channels +- $dY_2$ and $dY_3$ are the sizes of the output for the two spatial axes + +#### Constraints. +- `[C1]`: Consistency between the shape of tensors $Y$, $X$, and attributes `kernel_shape`, `pads`, `dilations` and `strides` + - Statement: + - $dY_2 = \left\lfloor{((dX_2 + pad\_shape[0] - (\texttt{dilations}[0] * (\texttt{kernel\_shape}[0] - 1) + 1)) / \texttt{strides}[0]) + 1}\right\rfloor$ + - $dY_3 = \left\lfloor{((dX_3 + pad\_shape[1] - (\texttt{dilations}[1] * (\texttt{kernel\_shape}[1] - 1) + 1)) / \texttt{strides}[1]) + 1}\right\rfloor$ + - where $pad\_shape[i]$ is the sum of the pads along spatial axis $i$ + - In the previous formulae, `ceil_mode`(#ceil_mode) is considered set to 0 ([see `ceil_mode` definition](#ceil_mode)). +- `[C2]`: $dY_2$ and $dY_3$ positive. + - Statement: + - $\left\lfloor{(dX_2 + pad\_shape[0] - 1 - n*\texttt{strides}[0]) / \texttt{dilations}[0]}\right\rfloor + 2 \le \texttt{kernel\_shape}[0] \le \left\lfloor{(dX_2 + pad\_shape[0] - 1 - (n-1)*\texttt{strides}[0]) / \texttt{dilations}[0]}\right\rfloor + 1$ + - $\left\lfloor{(dX_3 + pad\_shape[1] - 1 - n*\texttt{strides}[1]) / \texttt{dilations}[1]}\right\rfloor + 2 \le \texttt{kernel\_shape}[1] \le \left\lfloor{(dX_3 + pad\_shape[1] - 1 - (n-1)*\texttt{strides}[1]) / \texttt{dilations}[1]}\right\rfloor + 1$ + - where $pad\_shape[i]$ is the sum of the pads along spatial axis $i$ + - Rationale: TBD. + + + +### $\text{Indices}$: int64 + +$\textit{Indices}$ contains the indices of the input tensor $X$ from which the max values are taken. + +#### Constraints + + - `[C1]` First constraint on $\textit{Indices}$ + - Statement: $\textit{Indices}$ and $Y$ shall have the same shape + + +# **MaxPool** (float) +where float is in {float16, float, double} + + +## Signature +($Y, \textit{Indices}) = \textbf{MaxPool}(X)$ + +where: +- $X$: Input tensor +- $Y$: Output tensor containing max value selected from $X$ +- $\textit{Indices}$: Output tensor containing the indices in $X$ from where the max values are taken. + +## Restrictions + +See [Restrictions](#restrictions). + + +## Informal specification + +Operator **MaxPool** consumes an input tensor $X$ and applies max pooling across the tensor according to the kernel shape, strides, dilations and pads. Max pooling consists of computing the max on all values of a subset of the input tensor according to the kernel shape and downsampling the data into the output tensor $Y$. + +**MaxPool** is a sliding window operator like **Conv**, for instance. At a given position, the sliding window, called "kernel" or "$W$" in this document, is only there to select the set of elements of $X$ of which the maximum shall be computed. Therefore, only the shape of the kernel matters for **MaxPool**. + +Operator **MaxPool** stores in $\textit{Indices}$ the indices of the input tensor $X$ from which the max values are taken. The index values are those of a flatten 1-D view of $X$. + +In any position of the kernel over $X_p$, if the max value is present more than once, then the corresponding index value in $\textit{Indices}$ points the most upper left max value. + +The mathematical definition of output $Y$ and $\textit{Indices}$ are given hereafter: + +$$\begin{gathered} + Y[b, c, m, n] = \text{max}_{h=0}^{dW_0-1} \text{max}_{w=0}^{dW_1-1} \\ X_p[b,c,m \cdot \text{strides}[0]+ h \cdot \text{dilations}[0], n \cdot \text{strides}[1]+ w \cdot \text{dilations}[1] ] +\end{gathered}$$ + +$$\begin{gathered} + \textit{Indices}[b, c, m, n] = (h \cdot dX_3 + w) ~~\text{if}~~ Y[b, c, m, n] = X[h,w] +\end{gathered}$$ + + +Where +- $h \in [0,dX_2-1]$ is the index on the first spatial axis of $X_p$, whose dimension is $dX_2$. +- $w \in [0,dX_3-1]$ is the index on the second spatial axis of $X_p$, whose dimension is $dX_3$. +- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$ +- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$ +- $m \in [0,dY_2-1]$ is the index along the first spatial axis of output $Y$ +- $n \in [0,dY_3-1]$ is the index along the second spatial axis of output $Y$ +- $dW_0$ is the dimension of the first spatial axis of the kernel, i.e., the first value of attribute `kernel_shape` +- $dW_1$ is the dimension of the second spatial axis of the kernel, i.e., the second value of attribute `kernel_shape` +- `strides` is an attribute of the operator. It will be described later in this section. +- `dilation` is an attribute of the operator. It will be described later in this section. +- $X_{p} = \text{pad}(X,pads)$ is the padded version of the input tensor $X$. Function $\text{pad}$ applies padding as specified in section [pads](#pad_const_float_val). + + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +$\textit{Y},\textit{Indices} = \text{MaxPool}(X)$ + +- Data type: double +- Shape of $X$ = [1, 1, 3, 3] +- kernel_shape = [2,2] +- pads = [0,0,0,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 2, 2] +- Shape of $\textit{Indices}$ = [1, 1, 2, 2] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 1.70792822 & 1.59383029 & 2.22933891 \\ + 1.39774388 & 2.03411151 & 3.15139065 \\ + 2.81201102 & 5.85721996 & 3.55039159 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 2.03411151 & 3.15139065 \\ + 5.85721996 & 5.85721996 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 4 & 5 \\ + 7 & 7 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 2 + +$\textit{Y},\textit{Indices} = \text{MaxPool}(X)$ + +- Data type: double +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [1,0,1,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 4, 2] +- Shape of $\textit{Indices}$ = [1, 1, 4, 2] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 2.41529657 & 0.12586645 & 5.17877496 \\ + 5.82770299 & 3.77328965 & 3.51988829 \\ + 1.40679595 & 3.95043140 & -1.37421443 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 2.41529657 & 5.17877496 \\ + 5.82770299 & 5.17877496 \\ + 5.82770299 & 3.95043140 \\ + 3.95043140 & 3.95043140 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 2 \\ + 3 & 2 \\ + 3 & 7 \\ + 7 & 7 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 3 + +> Rajouter la contrainte sur la taille du padding (inférieure taille kernel) + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: double +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [0,0,0,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 2, 2] +- Shape of $\textit{indices}$ = [1, 1, 2, 2] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -inf & -inf & 4.56432533 \\ + -inf & -inf & 2.55354471 \\ + 2.83691720 & 3.46789489 & 5.23979851 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -inf & 4.56432533e+000 \\ + 3.46789489e+000 & 5.23979851e+000 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 2 \\ + 7 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 4 + +$\textit{Y},\textit{Indices} = \text{MaxPool}(X)$ + +- Data type: double +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [1,1,1,1] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 4, 4] +- Shape of $\textit{Indices}$ = [1, 1, 4, 4] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -inf & 9.57875561 & 4.56432533 \\ + 2.72844928 & 3.54234851 & 2.55354471 \\ + 2.83691720 & 3.46789489 & 5.23979851 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -inf & 9.57875561e+000 & 9.57875561e+000 & 4.56432533e+000 \\ + 2.72844928e+000 & 9.57875561e+000 & 9.57875561e+000 & 4.56432533e+000 \\ + 2.83691720e+000 & 3.54234851e+000 & 5.23979851e+000 & 5.23979851e+000 \\ + 2.83691720e+000 & 3.46789489e+000 & 5.23979851e+000 & 5.23979851e+000 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 1 & 2 \\ + 3 & 1 & 1 & 2 \\ + 6 & 4 & 8 & 8 \\ + 6 & 7 & 8 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Discrepancies observed in an existing implementations + +**WARNING: Non compliances with the SONNX specification have been observed on the ONNX Runtime implementation (version 1.23.2).** + +> S'assurer que les tests suivants ont été exécutés sur la bonne version d'ORT. + +Non compliance concern both the max values ($Y$) and the indices ($\text{indices}$). Refer to this [jupyter notebook](../../tests/maxpool/maxpool_divergence.ipynb) for further details on the observed problems. + +> Mettre les exemples dans `maxpool_doc.ypnb`. + +For instance, example 3 above executed on ONNX runtime with CPU provider produces the following output tensors: + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -1.79769313e+308 & 4.56432533e+000 \\ + 3.46789489e+000 & 5.23979851e+000 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -4 & 2 \\ + 7 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +Two discrepancies appear: +- in $Y$: -1.79769313e+308 instead of -inf as first element. +- in $\textit{Indices}$: $-4$ instead of $0$ (first element of $X$). + +## Error conditions +No error conditions. + +## Attributes + +For all attributes except `pads`, see section [[MaxPool(real)->Attributes]](#real_attributes). + +### $pads$: list of ints + +For the structural definition of `pads`, see [[MaxPool(real)->Attributes->pads]](#real_pads). + + +The constant float value to pad is -inf. + + +## Inputs + +### $\text{X}$: floating-point tensor + +$X$ is the input tensor from which the max values are selected. + + +#### Constraints + +See constraint [[C1]](#C1ia) on MaxPool(real) Input $X$. +See constraint [[C1]](#shape_consist) on MaxPool(real) Output $Y$. + + +## Outputs + +### $\text{Y}$: floating-point tensor + +The size of the output $Y$ will be $(dY_0 , dY_1 , dY_2 , dY_3)$ where +- $dY_0$ is the number of batches +- $dY_1$ is the number of channels +- $dY_2$ and $dY_3$ are the sizes of the output for the two spatial axes + +#### Constraints. + +See constraint [[C1]](#shape_consist) on MaxPool(real) Output $Y$. + +### $\text{Indices}$: int64 + + +$\textit{Indices}$ contains the indices of the input tensor $X$ from which the max values are taken. + +#### Constraints + + - `[C1]` First constraint on $\textit{Indices}$ + - Statement: $\textit{Indices}$ and $Y$ shall have the same shape + + +# **MaxPool** (int) +where int is in {int8, uint8}. + +## Signature + +($Y, \textit{Indices}) = \text{MaxPool}(X)$ + +where: +- $X$: input tensor +- $Y$: output tensor containing max value selected from $X$ +- $\textit{Indices}$: output tensor containing the indices in $X$ from where the max values are taken. + +## Restrictions + +See [Restrictions](#restrictions). + + +## Informal specification + +Operator **MaxPool** consumes an input tensor $X$ and applies max pooling across the tensor according to the kernel shape, strides, dilations and pads. Max pooling consists of computing the max on all values of a subset of the input tensor according to the kernel shape and downsampling the data into the output tensor $Y$. + +**MaxPool** is a sliding window operator like **Conv**, for instance. At a given position, the sliding window, called "kernel" or "$W$" in this document, is only there to select the set of elements of $X$ of which the maximum shall be computed. Therefore, only the shape of the kernel matters for **MaxPool**. + +Operator **MaxPool** stores in $\textit{Indices}$ the indices of the input tensor $X$ from which the max values are taken. The index values are those of a flatten 1-D view of $X$. + +In any position of the kernel over $X_p$, if the max value is present more than once, then the corresponding index value in $\textit{Indices}$ points the most upper left max value. + +The mathematical definition of output $Y$ and $\textit{Indices}$ are given hereafter: + +$$\begin{gathered} + Y[b, c, m, n] = \text{max}_{h=0}^{dW_0-1} \text{max}_{w=0}^{dW_1-1} \\ X_p[b,c,m \cdot \text{strides}[0]+ h \cdot \text{dilations}[0], n \cdot \text{strides}[1]+ w \cdot \text{dilations}[1] ] +\end{gathered}$$ + +$$\begin{gathered} + \textit{Indices}[b, c, m, n] = (h \cdot dX_3 + w) ~~\text{if}~~ Y[b, c, m, n] = X[h,w] +\end{gathered}$$ + + +Where +- $h \in [0,dX_2-1]$ is the index on the first spatial axis of $X_p$, whose dimension is $dX_2$. +- $w \in [0,dX_3-1]$ is the index on the second spatial axis of $X_p$, whose dimension is $dX_3$. +- $b \in [0,dY_0-1]$ is the batch index. $dY_0$ is the batch size of output $Y$ +- $c \in [0,dY_1-1]$ is the data channel. $dY_1$ is the number of data channels of output $Y$ +- $m \in [0,dY_2-1]$ is the index along the first spatial axis of output $Y$ +- $n \in [0,dY_3-1]$ is the index along the second spatial axis of output $Y$ +- $dW_0$ is the dimension of the first spatial axis of the kernel, i.e., the first value of attribute `kernel_shape` +- $dW_1$ is the dimension of the second spatial axis of the kernel, i.e., the second value of attribute `kernel_shape` +- `strides` is an attribute of the operator. It will be described later in this section. +- `dilation` is an attribute of the operator. It will be described later in this section. +- $X_{p} = \text{pad}(X,pads)$ is the padded version of the input tensor $X$. Function $\text{pad}$ applies padding as specified in section [pads](#pad_const_int_val). + + +The effect of the operator is illustrated on the following examples. + +### Example 1: + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: int8 +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [0,0,0,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 2, 2] +- Shape of $\textit{Indices}$ = [1, 1, 2, 2] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -12 & -13 & 5 \\ + -14 & -15 & 6 \\ + 7 & 8 & -1 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -12 & 6 \\ + 8 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 5 \\ + 7 & 7 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 2: + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: int8 +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [0,0,0,0] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 2, 2] +- Shape of $\textit{Indices}$ = [1, 1, 2, 2] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -128 & -128 & 5 \\ + -128 & -128 & 6 \\ + 7 & 8 & -1 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -128 & 6 \\ + 8 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 5 \\ + 7 & 7 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 3 + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: int8 +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [0,1,1,1] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 3, 4] +- Shape of $\textit{Indices}$ = [1, 1, 3, 4] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 1 & 2 & 0 \\ + 0 & 3 & 5 \\ + -2 & 5 & 6 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 1 & 3 & 5 & 5 \\ + 0 & 5 & 6 & 6 \\ + -2 & 5 & 6 & 6 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 4 & 5 & 5 \\ + 3 & 7 & 8 & 8 \\ + 6 & 7 & 8 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 4 + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: int8 +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [1,1,1,1] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 4, 4] +- Shape of $\textit{Indices}$ = [1, 1, 4, 4] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -128 & -127 & 5 \\ + -128 & -127 & 6 \\ + 7 & 8 & -128 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -128 & -127 & 5 & 5 \\ + -128 & -127 & 6 & 6 \\ + 7 & 8 & 8 & 6 \\ + 7 & 8 & 8 & -128 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 2 \\ + 3 & 1 & 5 & 5 \\ + 6 & 7 & 7 & 5 \\ + 6 & 7 & 7 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +### Example 5 + +$\textit{Y},\textit{indices} = \text{MaxPool}(X)$ + +- Data type: uint8 +- Shape of $X$ = [1, 1, 3, 3] +- kernel\_shape = [2,2] +- pads = [1,1,1,1] +- dilation = [1,1] +- strides = [1,1] +- Shape of $Y$ = [1, 1, 4, 4] +- Shape of $\textit{Indices}$ = [1, 1, 4, 4] + + +```math +X = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 5 \\ + 1 & 1 & 6 \\ + 7 & 8 & 0 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 5 & 5 \\ + 1 & 1 & 6 & 6 \\ + 7 & 8 & 8 & 6 \\ + 7 & 8 & 8 & 0 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 2 \\ + 3 & 1 & 5 & 5 \\ + 6 & 7 & 7 & 5 \\ + 6 & 7 & 7 & 8 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + + +### Discrepancies observed in an existing implementation + +> Reporter les modifs ci-dessus. + +Runnning Example 4 above on ONNX runtime with CPU as provider produces the following $\textit{Indices}$ output tensor: + +```math +\textit{Indices} = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + -4 & 1 & 2 & 2 \\ + -4 & 1 & 5 & 5 \\ + 6 & 7 & 7 & 5 \\ + 6 & 7 & 7 & -4 \\ + \end {bmatrix} + \end {bmatrix} +\end {bmatrix} +``` + +The following discreapancies of the same kind appear in it: +- $-4$ instead of $0$ (first element of $X$). +- $-4$ instead of $3$ (fourth element of $X$). +- $-4$ instead of $8$ (ninth element of $X$). + + +## Error conditions +No error conditions. + +## Attributes + +For all attributes except `pads`, see section [[MaxPool(real)->Attributes]](#real_attributes). + +### $pads$: list of ints + +For the structural definition of `pads`, see [[MaxPool(real)->Attributes->pads]](#real_pads). + + +The integer const value to pad is: +- $-128$ for int8 +- $0$ for uint8. + +## Inputs + +### $\text{X}$: floating-point tensor + +$X$ is the input tensor from which the max values are selected. + + +#### Constraints + +See constraint [[C1]](#C1ia) on MaxPool(real) Input $X$. +See constraint [[C1]](#shape_consist) on MaxPool(real) Output $Y$. + + +## Outputs + +### $\text{Y}$: floating-point tensor + +The size of the output $Y$ will be $(dY_0 , dY_1 , dY_2 , dY_3)$ where +- $dY_0$ is the number of batches +- $dY_1$ is the number of channels +- $dY_2$ and $dY_3$ are the sizes of the output for the two spatial axes + +#### Constraints. + +See constraint [[C1]](#shape_consist) on MaxPool(real) Output $Y$. + +### $\text{Indices}$: int64 + +$\textit{Indices}$ contains the indices of the input tensor $X$ from which the max values are taken. + +#### Constraints + + - `[C1]` First constraint on $\textit{Indices}$ + - Statement: $\textit{Indices}$ and $Y$ shall have the same shape diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/hypothesis/test_maxpool.py b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/hypothesis/test_maxpool.py new file mode 100644 index 00000000..a78417eb --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/hypothesis/test_maxpool.py @@ -0,0 +1,345 @@ +""" +Using hypothesis to generate automatic tests for MaxPool operator (SONNX) +""" +import math +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +#AUTO_PAD_OPTIONS = ["NOTSET", "VALID", "SAME_UPPER", "SAME_LOWER"] +AUTO_PAD_OPTIONS = ["NOTSET"] + +""" +Function to generate valid inputs/atributes for Conv operator +""" +@st.composite +def valid_maxpool_args(draw): + # x and w dimensions + dx0 = draw(st.integers(min_value=1, max_value=1)) +# dx1 = draw(st.integers(min_value=256, max_value=256)) + dx1 = draw(st.integers(min_value=2, max_value=2)) +# dx2 = draw(st.integers(min_value=32, max_value=256)) + dx2 = draw(st.integers(min_value=8, max_value=8)) +# dx3 = draw(st.integers(min_value=32, max_value=256)) + dx3 = draw(st.integers(min_value=8, max_value=8)) +# dw0 = draw(st.integers(min_value=1, max_value=dx2)) +# dw1 = draw(st.integers(min_value=1, max_value=dx3)) + dw0 = draw(st.integers(min_value=3, max_value=3)) + dw1 = draw(st.integers(min_value=3, max_value=3)) + + + # x + min_value = -10000.0 + max_value = 10000.0 + a_numeric = st.floats(min_value=min_value, max_value=max_value) + x = draw(hnp.arrays(dtype=np.float32, shape=(dx0, dx1, dx2, dx3), elements=a_numeric)) + + + # Atributes +# pads = draw(st.lists( +# st.integers(min_value=0, max_value=1000), min_size=4, max_size=4) + +# )#FIXME: Check this Max Value +# pads_0 = draw(st.integers(min_value=0, max_value=(dw0-1))) +# pads_2 = draw(st.integers(min_value=0, max_value=(dw0-1))) +# pads_1 = draw(st.integers(min_value=0, max_value=(dw1-1))) +# pads_3 = draw(st.integers(min_value=0, max_value=(dw1-1))) + pads_0 = draw(st.integers(min_value=0, max_value=(1))) + pads_2 = draw(st.integers(min_value=0, max_value=(1))) + pads_1 = draw(st.integers(min_value=0, max_value=(1))) + pads_3 = draw(st.integers(min_value=0, max_value=(1))) + pads = [pads_0, pads_1, pads_2, pads_3] +# strides = draw(st.lists(st.integers(min_value=1, max_value=1000), min_size=2, max_size=2) + strides = draw(st.lists(st.integers(min_value=1, max_value=1), min_size=2, max_size=2) + ) #FIXME: Check this Max Value + auto_pad = draw(st.sampled_from(AUTO_PAD_OPTIONS)) + kernel_shape = [dw0, dw1] + + # Auxiliary variables + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + line_dilation_max = math.floor((myalpha-1) /(dw0 -1)) if dw0 > 1 else 1 + column_dilation_max = math.floor((mybeta-1) /(dw1 -1)) if dw1 > 1 else 1 + line_dilation_value = draw(st.integers(min_value=1, max_value=line_dilation_max)) + column_dilation_value = draw(st.integers(min_value=1, max_value=column_dilation_max)) + + # Atributes + dilation = [line_dilation_value, column_dilation_value] + + return x, pads, strides, dilation, auto_pad, kernel_shape + +""" +Run ONNX runtime with generated inputs/atributes and check constraints +""" +@settings(max_examples= 1000,deadline=None) +@given(valid_maxpool_args()) +def test_maxpool(args): + print("--------------------------------------------------") + x, pads, strides, dilation, auto_pad, kernel_shape = args + dx0, dx1, dx2, dx3 = x.shape + dw0, dw1 = kernel_shape + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw0 - 1)) + 1 + mygamma = (dilation[1] * (dw1 - 1)) + 1 +#dY_2 = \left\lfloor{(dX_2 + pad\_shape[0] - dilations[0] * (kernel\_shape[0] - 1) - 1) / strides[0] + 1}\right\rfloor +#Where: pad_shape[i] is the sum of the pads along spatial axis i +# mydy2 = math.floor((myalpha - (mytheta)) / strides[0])) + 1 + mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1) +#dY_3 = \left\lfloor{(dX_3 + pad\_shape[1] - dilations[1] * (kernel\_shape[1] - 1) - 1) / strides[1] + 1} \right\rfloor +# where: pad_shape[i] is the sum of the pads along spatial axis i +# mydy3 = math.floor((mybeta - (mygamma)) / strides[1]) + 1 + mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1) + +# x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, dx2, dx3]) + x_onnx = helper.make_tensor_value_info('x_onnx', helper.np_dtype_to_tensor_dtype(x.dtype), [dx0, dx1, dx2, dx3]) + + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'MaxPool', + ['x_onnx'], + ['y_Onnx', 'indices_Onnx'], + dilations=dilation, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + ) + else: + node_def = helper.make_node( + 'MaxPool', + ['x_onnx'], + ['y_Onnx', 'indices_Onnx'], + dilations=dilation, + kernel_shape=[dw0, dw1], + strides=strides, + auto_pad=auto_pad, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-maxpool', + [x_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dx1, mydy2, mydy3]), + helper.make_tensor_value_info('indices_Onnx', TensorProto.INT64, [dx0, dx1, mydy2, mydy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), providers=["CPUExecutionProvider"]) + +#2026-01-06 09:44:30.219581227 [E:onnxruntime:, inference_session.cc:2280 operator()] Exception during initialization: +# /onnxruntime_src/onnxruntime/core/providers/cpu/nn/pool_attributes.h:78 +# onnxruntime::PoolAttributes::PoolAttributes(const onnxruntime::OpNodeProtoHelper&, +# const std::string&, int) pads[dim] < kernel_shape[dim] && pads[dim + kernel_shape.size()] < kernel_shape[dim] was false. +# Pad should be smaller than kernel. + + # Initialize tensors + x = x.reshape(dx0, dx1, dx2, dx3).astype(np.float32) + + print("Pads", pads) + print("Strides", strides) + print("Dilation", dilation) + print("Auto_pad", auto_pad) + y = sess.run(None, {'x_onnx': x})[0] + indices = sess.run(None, {'x_onnx': x})[1] + + print("x shape:", x.shape) + print("x:", x) + +# padded_x = pad_4d_spatial_js(x, pads) +# print ("padded_x:", padded_x) + + print("w shape:", kernel_shape) + #print("w:", w) + + print("mydy2:", mydy2) + print("mydy3:", mydy3) + print("Y shape:", y.shape) + print("Indices shape:", indices.shape) + print("Y:", y) + + check_constraints(x, auto_pad, y, mydy2, mydy3, kernel_shape, strides, node_def, pads, dilation) + +def check_constraints(x, auto_pad, y, mydy2, + mydy3, kernel_shape, strides, + node_def, pads, dilation): + + #x - Constraints + # C1 + assert x.ndim == 4 and x.shape[2] >= 0 and x.shape[3] >= 0 + # C2: see C2 of y + + assert all(dim > 0 for dim in x.shape) + + + #w - Constraints + # TBD + assert len(kernel_shape) == 2 + assert all(dim > 0 for dim in kernel_shape) + + #Strides - Constraints + # C1 + assert all(s > 0 for s in strides) + # C2 + assert len(strides) == 2 + + + #Auto_pad - Constraints + #C1 + assert auto_pad in AUTO_PAD_OPTIONS + + #Pads - Constraints + # C1 + assert all(p >= 0 for p in pads) + # C2 + assert len(pads) == (x.ndim - 2) * 2 + # C3 + # Same of C3 of x + + + #Dilation - Constraints + # C1 + assert all(d > 0 for d in dilation) + # C2 + assert len(dilation) == len(kernel_shape) + # C3 + # Same of C3 of x + + + #kernel shape - Constraints + # C1 + for kernel_value in kernel_shape: + assert kernel_value > 0 + # C2 + + + #Y - Constraints + # C1 + #Same of C3 of x + + #Functional check + my_y = MaxPool(x, kernel_shape, strides, dilation, pads) + print("my_y:", my_y) + assert np.array_equal(y, my_y) + +def compute_MaxPool_output_shape(input_shape, kernel_shape, pads, dilation, strides): + + dx0, dx1, dx2, dx3 = input_shape + dw0, dw1 = kernel_shape + + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw0 - 1)) + 1 + mygamma = (dilation[1] * (dw1 - 1)) + 1 + mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1) + mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1) + + output_shape = [dx0, dx1, mydy2, mydy3] + + return output_shape + + +def pad_4d_spatial_js(tensor, paddings): + """ + Pads the last two dimensions (H, W) of a 4D tensor. + paddings = (top, left, bottom, right) + """ + N, C, H, W = tensor.shape + top, left, bottom, right = paddings + + # 1. Calculate the new spatial dimensions + new_H = H + top + bottom + new_W = W + left + right + + # 2. Create the new 4D "canvas" of zeros + # The Batch (N) and Channel (C) dimensions remain the same +# padded_tensor = np.zeros((N, C, new_H, new_W), dtype=tensor.dtype) + padded_tensor = np.full((N, C, new_H, new_W), -np.inf) + + # 3. Insert the original tensor into the padded one + # We use ":" for N and C to select all batches and channels + # We use slicing for the H and W dimensions + padded_tensor[:, :, top : top + H, left : left + W] = tensor + + return padded_tensor + +def extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilations, dW): + """ + Extracts the maximum value from a dilated window. + + Args: + X_p: The 2D padded input array. + m, n: Integer constants representing the current output position. + strides: Tuple (s_h, s_w) for step size. + dilations: Tuple (d_h, d_w) for spacing between window elements. + dW: Tuple (dW0, dW1) for the window height and width. + """ + dW0, dW1 = dW + s_h, s_w = strides + d_h, d_w = dilations + + # Calculate the base (top-left) coordinates based on the stride + base_h = m * s_h + base_w = n * s_w + + # Initialize max_val with the first element of the window (h=0, w=0) + max_val = X_p[b, c, base_h, base_w] + + # Iterate through the window defined by dW0 and dW1 + for h in range(dW0): + for w in range(dW1): + # Calculate the current dilated coordinates + curr_h = base_h + h * d_h + curr_w = base_w + w * d_w + + # Extract the value at these coordinates + val = X_p[b, c, curr_h, curr_w] + + # Update the maximum found so far + if val > max_val: + max_val = val + + return max_val + +def MaxPool(X, kernel_shape, strides, dilation, pads): + + Y_dims = compute_MaxPool_output_shape(X.shape, kernel_shape, pads, dilation, strides) + + Y = np.zeros(Y_dims) + + X_p = pad_4d_spatial_js(X, pads) + dX_p0, dX_p1, dX_p2, dX_p3 = X_p.shape + dY_p0, dY_p1, dY_p2, dY_p3 = Y.shape + + print("X_p:", X_p) + + for b in range(dX_p0): + for c in range(dX_p1): + for m in range(dY_p2): + for n in range(dY_p3): + Y[b, c, m, n] = extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilation, kernel_shape) + + return Y \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_1.ipynb b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_1.ipynb new file mode 100644 index 00000000..127a72b3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_1.ipynb @@ -0,0 +1,69 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "23d64d35", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "tensor([[[[6., 8.],\n", + " [3., 4.]]]])" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import torch\n", + "import torch.nn as nn\n", + "\n", + "# Define the input tensor\n", + "input_tensor = torch.tensor(\n", + " [\n", + " [1, 1, 2, 4],\n", + " [5, 6, 7, 8],\n", + " [3, 2, 1, 0],\n", + " [1, 2, 3, 4]\n", + " ], dtype = torch.float32)\n", + "\n", + "# Reshape the input_tensor\n", + "input_tensor = input_tensor.reshape(1, 1, 4, 4)\n", + "\n", + "# Initialize the Max-pooling layer with kernel 2X2 and stride 2\n", + "pool = nn.MaxPool2d(kernel_size=2, stride=2)\n", + "\n", + "# Apply the Max-pooling layer to the input tensor\n", + "output = pool(input_tensor)\n", + "\n", + "# Print the output tensor\n", + "output" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_onnx.ipynb b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_onnx.ipynb new file mode 100644 index 00000000..99663bb9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/maxpool/tests/maxpool_onnx.ipynb @@ -0,0 +1,217 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "U5A9c4B_s72W" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (1.13.0)\r\n", + "Requirement already satisfied: onnxruntime in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (1.13.1)\r\n", + "Requirement already satisfied: numpy>=1.16.6 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (1.24.1)\r\n", + "Requirement already satisfied: protobuf<4,>=3.20.2 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (3.20.3)\r\n", + "Requirement already satisfied: typing-extensions>=3.6.2.1 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (4.13.2)\r\n", + "Requirement already satisfied: coloredlogs in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (15.0.1)\r\n", + "Requirement already satisfied: flatbuffers in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (25.9.23)\r\n", + "Requirement already satisfied: packaging in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (22.0)\r\n", + "Requirement already satisfied: sympy in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (1.11.1)\r\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from coloredlogs->onnxruntime) (10.0)\r\n", + "Requirement already satisfied: mpmath>=0.19 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from sympy->onnxruntime) (1.2.1)\r\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from onnx import TensorProto\n", + "from onnx.helper import (\n", + " make_model, make_node, make_graph,\n", + " make_tensor_value_info)\n", + "from onnx.checker import check_model" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fc6VkO6dfbuD", + "outputId": "67515005-b301-4a66-ba77-413564e2194f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "graph test-maxpool (\n", + " %x[DOUBLE, 1x1x8x8]\n", + ") {\n", + " %y, %Indices = MaxPool[auto_pad = 'NOTSET', dilations = [1, 1], kernel_shape = [3, 3], pads = [0, 0, 0, 0], strides = [1, 1]](%x)\n", + " return %y, %Indices\n", + "}\n", + "X shape: (1, 1, 8, 8)\n", + "X: [[[[ 5.67591154 -0.04859958 2.94203104 3.70327292 2.47014306\n", + " 4.12455586 5.81838665 1.84118807]\n", + " [-0.05267874 2.75227858 2.16608732 4.03416243 1.28184638\n", + " 4.81748948 4.64878412 3.31626988]\n", + " [ 3.55427648 0.39997585 4.45761508 4.82722666 0.18843372\n", + " 0.49564314 7.96647029 4.82851447]\n", + " [ 1.52417623 2.28965587 0.36251913 1.64413983 4.67267459\n", + " 3.73167179 2.20052118 2.06720836]\n", + " [-1.22446366 -0.86469519 6.01461967 -1.08813165 2.11920055\n", + " 0.78561867 0.29834533 1.94499626]\n", + " [ 1.57776732 3.64260188 3.47181319 4.83723727 1.49868674\n", + " 3.27683692 2.42625178 0.4401565 ]\n", + " [ 6.8972704 5.51113868 5.99293336 4.24088721 1.94993561\n", + " -0.04040625 3.07940675 3.06769141]\n", + " [ 3.1299626 4.5546675 3.5008191 2.06181403 3.27400104\n", + " 6.70386189 0.92777015 -1.29092574]]]]\n", + "Y shape: (1, 1, 6, 6)\n", + "Y: [[[[5.67591154 4.82722666 4.82722666 4.82722666 7.96647029 7.96647029]\n", + " [4.45761508 4.82722666 4.82722666 4.82722666 7.96647029 7.96647029]\n", + " [6.01461967 6.01461967 6.01461967 4.82722666 7.96647029 7.96647029]\n", + " [6.01461967 6.01461967 6.01461967 4.83723727 4.67267459 3.73167179]\n", + " [6.8972704 6.01461967 6.01461967 4.83723727 3.27683692 3.27683692]\n", + " [6.8972704 5.99293336 5.99293336 6.70386189 6.70386189 6.70386189]]]]\n", + "Indices shape: (1, 1, 6, 6)\n", + "Indices: [[[[ 0 19 19 19 22 22]\n", + " [18 19 19 19 22 22]\n", + " [34 34 34 19 22 22]\n", + " [34 34 34 43 28 29]\n", + " [48 34 34 43 45 45]\n", + " [48 50 50 61 61 61]]]]\n" + ] + } + ], + "source": [ + "# @title Standard max pooling (1 channel)\n", + "import numpy as np\n", + "from onnx import *\n", + "import onnxruntime\n", + "\n", + "# Create inputs\n", + "x = helper.make_tensor_value_info('x', TensorProto.DOUBLE, [1, 1, 8, 8])\n", + "\n", + "# Create a node (MaxPool) with input/outputs\n", + "node_def = make_node(\n", + " 'MaxPool', # node name\n", + " ['x'], # inputs\n", + " ['y', 'Indices'], # outputs\n", + " dilations=[1,1],\n", + " kernel_shape=[3,3],\n", + " pads=[0, 0, 0, 0],\n", + " strides=[1, 1],\n", + " auto_pad='NOTSET',\n", + "# group=1, # Standard convolution\n", + ")\n", + "\n", + "# Create the graph\n", + "graph_def = make_graph(\n", + " [node_def],\n", + " 'test-maxpool',\n", + " [x],\n", + " [helper.make_tensor_value_info('y', TensorProto.DOUBLE, [1, 1, 6, 6]), \n", + " helper.make_tensor_value_info('Indices', TensorProto.INT64, [1, 1, 6, 6])],\n", + ")\n", + "\n", + "onnx_model = make_model(graph_def)\n", + "\n", + "# Let's freeze the opset.\n", + "del onnx_model.opset_import[:]\n", + "opset = onnx_model.opset_import.add()\n", + "opset.domain = ''\n", + "opset.version = 15\n", + "onnx_model.ir_version = 8\n", + "\n", + "# Verify the model\n", + "check_model(onnx_model)\n", + "\n", + "# Print a human-readable representation of the graph\n", + "print(onnx.helper.printable_graph(onnx_model.graph))\n", + "\n", + "# Do inference\n", + "sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(),\n", + " providers=[\"CPUExecutionProvider\"])\n", + "\n", + "# Initialize tensors\n", + "#x = numpy.ones((1, 1, 8, 8), dtype=numpy.float32)\n", + "\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "\n", + "y = sess.run(None, {'x': x})[0]\n", + "Indices = sess.run(None, {'x': x})[1]\n", + "\n", + "print(\"X shape:\", x.shape)\n", + "print(\"X:\", x)\n", + "print(\"Y shape:\", y.shape)\n", + "print(\"Y:\", y)\n", + "print(\"Indices shape:\", Indices.shape)\n", + "print(\"Indices:\", Indices)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOKOYLaMLVHJNovPEUX24CO", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/safety-related-profile/sonnx/ops/spec/informal/mul/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/mul/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/mul/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/mul/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/mul/mul.md b/safety-related-profile/sonnx/ops/spec/informal/mul/mul.md new file mode 100644 index 00000000..da93be00 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/mul/mul.md @@ -0,0 +1,341 @@ +# Contents + - **Mul** operator for type [real](#real) + - **Mul** operator for types [float16, float, double](#float) + - **Mul** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation version 14. + +--- + + +# **Mul** (real, real) + +## Signature + +Definition of operator $\text{Mul}$ signature: + +$C = \text{Mul}(A, B)$ + +where: +- $A$: first operand of the multiplication +- $B$: second operand of the multiplication +- $C$: result of the element-wise multiplication of $A$ by $B$ + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Mul** operator. +## Informal specification + + +[E_MUL_REAL_FUNC_0010]
+ +Operator **Mul** multiplies input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. Each element $C[i]$ is the result of multiplying $A[i]$ by $B[i]$ where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The definition of the operator is given hereafter. + +For any index i: + +$$ +C[i] = A[i] \times B[i] +$$ + +The effect of the operator is illustrated on the following examples: +[END]
+ +--- + +### Example 1 (1D tensors) + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 2 & 3 & 4 \end{bmatrix} +``` + +```math +C = A \times B = \begin{bmatrix} 12.2 & 28.5 & 142.8 \end{bmatrix} +``` + +--- + +## Error conditions +No error condition. + +## Inputs + +### $\text{A}$: real tensor +Tensor $A$ is the first operand of the multiplication. + +#### Constraints + + + - `[E_MUL_REAL_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + + +### $\text{B}$: real tensor +Tensor $B$ is the second operand of the multiplication. + +#### Constraints + + - `[E_MUL_REAL_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_MUL_REAL_CONSTR_A_0010](#E_MUL_REAL_CONSTR_A_0010) on tensor $A$. + + +## Outputs + +### $\text{C}$: real tensor + +Tensor $C$ is the element-wise result of $A$ multiplied by $B$. + +#### Constraints + + - `[E_MUL_REAL_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_MUL_REAL_CONSTR_A_0010](#E_MUL_REAL_CONSTR_A_0010) on tensor $A$. + + +## Attributes + +The **Mul** operator has no attribute. + + +--- + + +# **Mul** (float, float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{mul}$ signature: + +$C = \text{Mul}(A, B)$ + +where + + - $A$: first operand tensor + - $B$: second operand tensor + - $C$: output tensor, result of element-wise multiplication of $A$ by $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Mul** operator. + +## Function + +[E_MUL_FLOAT_FUNC_0010]
+Operator **Mul** multiplies input tensors $A$ by $B$ element-wise according to IEEE 754 floating-point semantics and stores the result in output tensor $C$. If $i$ is a [tensor index](../common/definitions.md#tensor_index), each element $C[i]$ is the result of multiplying $A[i]$ by $B[i]$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +C[i] = A[i] \times B[i] +$$ + +[END]
+ +--- + +### Example 1 (2D tensors) + +```math +A = \begin{bmatrix} 3.0 & 4.5 \\ 16.0 & 1.0 \\ 25.5 & 24.25 \end{bmatrix} +\quad +B = \begin{bmatrix} 3.0 & 2.0 \\ 4.0 & 0.0 \\ 5.0 & 4.0 \end{bmatrix} +``` + +```math +C = A \times B = \begin{bmatrix} 9.0 & 9.0 \\ 64.0 & 0.0 \\ 127.5 & 97.0 \end{bmatrix} +``` +## Error conditions +No error condition. + +## Inputs + +### $\text{A}$: floating-point tensor +Tensor $A$ is the first operand of the multiplication. + +#### Constraints + + +- `[E_MUL_FLOAT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_MUL_FLOAT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: floating-point tensor +Tensor $B$ is the second operand of the multiplication. + +#### Constraints +- `[E_MUL_FLOAT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_MUL_FLOAT_CONSTR_A_0010](#E_MUL_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_MUL_FLOAT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_MUL_FLOAT_CONSTR_A_0020](#E_MUL_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: floating-point tensor + +Tensor $C$ is the element-wise result of $A$ multiplied by $B$. + +#### Constraints + + - `[E_MUL_FLOAT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_MUL_FLOAT_CONSTR_A_0010](#E_MUL_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_MUL_FLOAT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_MUL_FLOAT_CONSTR_A_0020](#E_MUL_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Attributes + +The **Mul** operator has no attribute. + +--- + + + +# **Mul** (int, int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature +Definition of operator $\text{mul}$ signature: + + $C = \text{Mul}(A,B)$ + + where + - $A$: first operand of the multiplication + - $B$: second operand of the multiplication + - $C$: result of the element-wise multiplication of $A$ by $B$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Mul** operator. + +## Function + + +[E_MUL_INT_FUNC_0010]
+ +Operator **Mul** multiplies input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. Each element $C[i]$ is the result of multiplying $A[i]$ by $B[i]$ where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The integer multiplication is performed as follows (considering that all tensors have the same type): + +For unsigned values (type uint\): + +$$ +C[i]= +\begin{cases} + A[i] \times B[i]- k.2^{n} & \quad \textrm{if } A[i] \times B[i] > 2^{n}-1 \\ + A[i] \times B[i] & \quad \textrm{otherwise} +\end{cases} +$$ + +with $k \in \mathbb{N}$ such that $0 \le A[i] \times B[i]- k.2^{n} \le 2^n-1$ + +For signed values (type int\): + +$$ +C[i]= +\begin{cases} + A[i] \times B[i]- k_1.2^{n} & \quad \textrm{if } A[i] \times B[i] > 2^{n-1}-1 \\ + A[i] \times B[i] + k_2.2^{n} & \quad \textrm{if } A[i] \times B[i] < -2^{n-1} \\ + A[i] \times B[i] & \quad \textrm{otherwise} +\end{cases} +$$ + +with: + +$k_1 \in \mathbb{N}$ such that $-2^{n-1} \le A[i] \times B[i]-k_1.2^{n} \le 2^{n-1}-1$ + +$k_2 \in \mathbb{N}$ such that $-2^{n-1} \le A[i] \times B[i]+k_2.2^{n} \le 2^{n-1}-1$ + +[END]
+ +### Example 1 (1D uint8 tensors) + +```math +A = \begin{bmatrix} 6 & 9 & 35 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 100 & 5 \end{bmatrix} +``` +```math +C = \begin{bmatrix} 18 & 132 & 175 \end{bmatrix} +``` + +### Example 2 (1D int8 tensors) + +```math +A = \begin{bmatrix} -6 & -9 & -9 & 9 \end{bmatrix} +\quad +B = \begin{bmatrix} -3 & 100 & -100 & 100 \end{bmatrix} +``` +```math +C = \begin{bmatrix} 18 & 124 & -124 & -124 \end{bmatrix} +``` + +## Error conditions +- According to the definition, the result of the multiplication differs from the value that would be expected in $\mathbb{N}$ (for unsigned) or $\mathbb{Z}$ (for signed) when under- or overflow occur. + +## Inputs + +### $\text{A}$: integer tensor + +Tensor $A$ is the first operand of the multiplication. + +#### Constraints +This section gives all constraints applicable to the input. + + +- `[E_MUL_INT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_MUL_INT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + + +### $\text{B}$: integer tensor + +Tensor $B$ is the second operand of the multiplication. + +#### Constraints + +- `[E_MUL_INT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_MUL_INT_CONSTR_A_0010](#E_MUL_INT_CONSTR_A_0010) on tensor $A$. +- `[E_MUL_INT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_MUL_INT_CONSTR_A_0020](#E_MUL_INT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: integer tensor + +Tensor $C$ is the element-wise integer multiplication result. + +#### Constraints + + - `[E_MUL_INT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_MUL_INT_CONSTR_A_0010](#E_MUL_INT_CONSTR_A_0010) on tensor $A$. +- `[E_MUL_INT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_MUL_INT_CONSTR_A_0020](#E_MUL_INT_CONSTR_A_0020) on tensor $A$. + +## Attributes + +The **Mul** operator has no attribute. + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/mul/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/mul/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/mul/reviews/jean.md b/safety-related-profile/sonnx/ops/spec/informal/mul/reviews/jean.md new file mode 100644 index 00000000..5d20eab5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/mul/reviews/jean.md @@ -0,0 +1,17 @@ +Rmk1: Typos. Taken into account in mul.md + +Rmk2: In ONNX, the name of the operator is **M**ub, not mul. Taken into account in mul.md + +Rmk3: Contents section. + +* ONNX bfloat16 type is in the ONNX description of Mul but not in SONNX mul.md. If this type is never used in SONNX, it has to be inserted as a general restriction. + +* INT4 and UINT4 are in SONNX mul.md but not in the ONNX description of Mul. Taken into account in mul.md. + +* Add reference to the ONNX documentation. Taken into account in mul.ld + +Rmk4: broken links. Already fixed. + +Rmk5: Replace the type names by their counterpart in ONNX. Example: replace "FP64" by "double". Taken into account in mul.md. + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/neg/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/neg/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/neg/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/neg/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/neg/neg.md b/safety-related-profile/sonnx/ops/spec/informal/neg/neg.md new file mode 100644 index 00000000..10aafe79 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/neg/neg.md @@ -0,0 +1,124 @@ +# Preliminary remarks + +## Types + +- Operators are first described for values in the domain of real numbers. Because the `Neg` operator outputs a tensor representing the negation of each element in the input tensor, the output is of the same type as the input tensor. The input `A` can be of various types including `tensor(bfloat16)`, `tensor(double)`, `tensor(float)`, `tensor(float16)`, `tensor(int16)`, `tensor(int32)`, `tensor(int64)`, `tensor(int8)`, `tensor(uint16)`, `tensor(uint32)`, `tensor(uint64)`, and `tensor(uint8)`. The dimension size of a tensor is defined by $N(tensor)$. + +# `Neg` operator + +### Restrictions + +The following restrictions apply to the `Neg` operator for the SONNX profile: +- The tensor `A` must have a defined shape `[R1]` +- The operator does not support sparse tensors `[R2]` +- All elements of input `A` shall have explicit numeric types `[R3]` +- No broadcasting is allowed for the tensor `A` `[R4]` + +### Signature + +`B = Neg(A)` + +where +- `A`: input tensor to be negated +- `B`: output tensor containing the negated values of `A` + +#### Informal specification + +The `Neg` operator computes the negation of each element in the input tensor `A`. For each element, the resulting tensor `B` contains the value that is the negation of the corresponding entry in `A`. + +The mathematical definition of the operator is given hereafter. + +$$ +B[i] = -A[i] +$$ + +Where +- $i$ is an index covering all dimensions of the tensor. + +The effect of the operator is illustrated on the following examples : +- `A` is a tensor holding numerical data + +Example 1: +```math +`A` = \begin{bmatrix} 2 & -3 & 7 \end{bmatrix} +``` +Result `B` will be: +```math +`B` = \begin{bmatrix} -2 & 3 & -7 \end{bmatrix} +``` + +Example 2: +```math +`A` = \begin{bmatrix} 1 & -2 \\ 4 & 0 \\ -5 & 6 \end{bmatrix} +``` +Result `B` will be: +```math +`B` = \begin{bmatrix} -1 & 2 \\ -4 & 0 \\ 5 & -6 \end{bmatrix} +``` + +Note in python is is equivalent to do : +```python +>>> import numpy as np +np.negative([[1,-2],[0,-4],[-8,4]]) +array([[-1, 2], + [ 0, 4], + [ 8, -4]]) +``` + + +#### Inputs and outputs + +##### `A` + +Tensor `A` is the input tensor to be negated. + +The shape of tensor `A` should be predefined and cannot be broadcasted `[R1]` `[R4]`. + +###### Constraints + +- (C1) Shape consistency + - Statement: The shape of `A` must be predefined. `[R1]` Broadcastable tensor is forbidden.`[R4]`. + +#### Outputs + +##### `B` + +Tensor `B` is the output tensor formed by element-wise negation of `A`. + +`B` will have the same shape as `A`. `[R1]` Broadcastable tensor is forbidden.`[R4]` + +###### Constraints + +- (C1) Shape consistency + - Statement: The shape of `B` will match the `A` shape. $N(B)=N(A)$ `[R1]` Broadcastable tensor is forbidden.`[R4]`. + +#### Attributes + +The `Neg` operator does not require any attributes. + +### Formal specification + +The formal specification of the `Neg` operator using the Why3 language[^1] is provided below. This specification ensures the consistency and desired behavior of the operator within the constraints described. + +```ocaml +(** + Specification of Neg operation on tensors. + *) + +module Neg + use int.Int + use map.Map + use utils.Same + use tensor.Shape + use tensor.Tensor + + let function neg (a : tensor 'a) : tensor 'a = + { + shape = a.shape ; + value = fun i -> - a.value[i] ; + } + +end +``` + +[^1]: See [Why3 documentation](https://www.why3.org/) diff --git a/safety-related-profile/sonnx/ops/spec/informal/neg/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/neg/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pad/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/pad/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pad/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/pad/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pad/pad.md b/safety-related-profile/sonnx/ops/spec/informal/pad/pad.md new file mode 100644 index 00000000..1708e8fd --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/pad/pad.md @@ -0,0 +1,23 @@ +# `pad` operator +### Contents +- `Pad` [operator (INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, BFLOAT16, FLOAT16, FLOAT, DOUBLE , STRING, BOOL)](#types) + +## `Pad` `(INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, BFLOAT16, FLOAT16, FLOAT, DOUBLE , STRING, BOOL)` + +### Signature +`output = Pad(data,pads,[mode],[constant_value])` +where +- `data`: input tensor +- `W`: convolution kernel +- `B`: optional bias +- `Y`: output tensor +#### Restrictions +The following restrictions apply to the `Pad` operator for the SONNX profile: +#### Informal specification +#### Error conditions +#### Inputs +#### Attributes +#### Outputs +#### Formal specification + +*(to be completed)* \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/pad/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/pad/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pow/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/pow/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pow/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/pow/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/pow/pow.md b/safety-related-profile/sonnx/ops/spec/informal/pow/pow.md new file mode 100644 index 00000000..7003e391 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/pow/pow.md @@ -0,0 +1,483 @@ +# Contents + +- **Pow** operator for type [real](#real) +- **Pow** operator for types [float16, float, double](#float) +- **Pow** operator for types [int32, int64](#int) + +Based on ONNX documentation [Pow version 15](https://onnx.ai/onnx/operators/onnx__Pow.html). + + + +# **Pow** (real, real) + +## Signature + +$C = \textbf{Pow}(A, B)$ + +where: + +- $A$: base tensor +- $B$: exponent tensor +- $C$: result of the element-wise power of $A$ by $B$ + +> Change A, B and C by X, Y, and Z. + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +## Informal specification + +Operator **Pow** computes the element-wise power of $A$ by $B$ and stores the result in output tensor $C$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +**pow** is undefined for the value $A[i] < 0 \text{ and } B[i] \notin \mathbb{Z} ~or~ B[i] = q/p \text{ in its simplest form with } q \text{ even} ~or~ A[i] = 0 \text{ et } B[i] \leq 0$ , otherwise **pow** is defined by : + +$$ +C[i] = +\begin{cases} +0 & \text{si } A[i] = 0 \text{ et } B[i] > 0 \\ +1 & \text{si } A[i] \neq 0 \text{ et } B[i] = 0\\ +e^{ B[i]\ln A[i]} = A[i]^{B[I]} & \text{otherwise} +\end{cases} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +A = \begin{bmatrix} 9 & 4 & 16 & 8 & 2 \end{bmatrix} +\quad +B = \begin{bmatrix} 2 & 2.5 & \frac{1}{2} & \frac{1}{3} & \frac{3}{2} \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 81 & 32 & 4 & 2 & 2.82842708 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 0 & 0 & 5 & -5 & -25 & -8 \end{bmatrix} +\quad +B = \begin{bmatrix} 0 & 2 & 0 & 0 & \frac{3}{5} & \frac{1}{3} \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 1 & 0 & 1 & 1 & −6.898648307 & -2.0 \end{bmatrix} +``` + + + +## Error conditions + +No error condition beyond the undefined behavior for out of range inputs in the mathematical model. + +## Attributes + +Operator **Pow** has no attribute. + +## Inputs + +### $\text{A}$: real tensor + +Base of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +### $\text{B}$: real tensor + +Exponent of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1ra_pow) on tensor $A$. + +## Outputs + +### $\text{C}$: real tensor + +Result of the element-wise power of $A$ to $B$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1ra_pow) on tensor $A$. + + + +# **Pow** (float, float) + +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Pow}$ signature: + +$C = \textbf{Pow}(A, B)$ + +where: + +- $A$: base tensor +- $B$: exponent tensor +- $C$: result of element-wise power of $A$ to $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +## Informal specification + +Operator **Pow** computes the element-wise power between input tensors $A$ and $B$ according to IEEE 754 floating-point semantics and stores the result in output tensor $C$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + + +$$ +C[i] = \text{pow}(A[i],B[i]) = +\begin{cases} +\text{NaN} +& +\begin{aligned} +&B[i]=\text{NaN} \\ +&\lor (A[i]=\text{NaN}\land B[i]\neq \pm0) \\ +&\lor (A[i]\in\mathbb{R}^-{<0}\land A[i]\neq -\inf\land B[i]\notin\mathbb{Z}) +\end{aligned}\\ +\\ +\\ +\\ ++\inf +& +\begin{aligned} +&B[i]=+\inf \land |A[i]|>1 \\ +&\lor (A[i]=+0\land B[i]\in\mathbb{Z}^- \land B[i]\in \mathbb{Z}\text{ odd}) \\ +&\lor (A[i]=\pm0\land B[i]<0\land B[i]\notin \mathbb{Z}\text{ odd}) \\ +&\lor (A[i]=+\inf\land B[i]>0) \\ +&\lor (A[i]=-\inf\land B[i]>0\land B[i]\notin \mathbb{Z}\text{ odd}) \\ +&\lor (B[i]=-\inf \land 0<|A[i]|<1 ) +\end{aligned}\\ +\\ +-\inf +& +\begin{aligned} +&(A[i]=-\inf\land B[i]\in\mathbb{Z}_{>0}\land B[i]\in \mathbb{Z}\text{ odd}) \\ +&\lor (A[i]=-0\land B[i]\in\mathbb{Z}_{<0}\land B[i]\in \mathbb{Z}\text{ odd}) +\end{aligned}\\ +\\ +\\ +\\ ++0 +& +\begin{aligned} +&B[i]=+\inf \land (0<|A[i]|<1) \\ +&\lor (A[i]=-\inf\land B[i]<0\land B[i]\notin \mathbb{Z}\text{ odd}) \\ +&\lor (A[i]=+0\land B[i]\in\mathbb{Z}_{>0}\land B[i]\in \mathbb{Z}\text{ odd})\\ +&\lor (A[i]=\pm0\land B[i]>0\land B[i]\notin \mathbb{Z}\text{ odd}) \\ +&\lor (A[i]=+\inf\land B[i]<0) \\ +&\lor (B[i]=-\inf \land |A[i]|>1) +\end{aligned}\\ +\\ +\\ +\\ +-0 +& +\begin{aligned} +&(A[i]=-0\land B[i]\in\mathbb{Z}_{>0}\land B[i]\in \mathbb{Z}\text{ odd})\\ +&\lor(A[i]=-\inf\land B[i]\in\mathbb{Z}_{<0}\land B[i]\in \mathbb{Z}\text{ odd}) +\end{aligned}\\ +\\ +\\ +\\ +1 +& +\begin{aligned} +&(A[i]=-1\land B[i]=\pm\inf) \\ +&\lor(A[i]=+1 ) \\ +&\lor(B[i]=\pm0 ) +\end{aligned}\\ +\\ +\\ +\\ +e^{B[i] \ln A[i]} = A[i]^{B[i]} +& +\text{otherwise} +\end{cases} +$$ + +> Review with respect to MPFR and IEEE. +> +> Rajouter une note pour indiquer l'extension vis-à-vis de l'IEEE. + +### Example 1 + +```math +A = \begin{bmatrix} 9.0 & 4.0 & 16.0 & 8.0 & 2.0 \end{bmatrix} +\\ +B = \begin{bmatrix} 2 & 2.5 & 0.5 & 0.33333333 & 1.5 \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 81.0 & 32.0 & 4.0 & 2.0 & 2.82842708 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 0.0 & 0.0 & 5.0 & -5.0 & -25.0 & -8.0 \end{bmatrix} +\\ +B = \begin{bmatrix} 0 & 2.0 & 0.0 & 0.0 & 0.6 & 0.33333333 \end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} 1.0 & 0.0 & 1.0 & 1.0 & NaN & NaN \end{bmatrix} +``` + +Note that $-8^{1/3} = -(2\times2\times2)^{1/3}= -2$ but $2^{0.33333333}$ gives a NaN. Some implementations do return -2. + +### Example 3 + +```math +A = \begin{bmatrix} +-2.0 & -2.0 & -1.0 & -1.0 & 0.0 & -0.0 & 2.0 & 0.5 & 2.0 +\end{bmatrix} +\\ +B = \begin{bmatrix} +0.5 & 3.0 & +\inf & -\inf & -3.0 & -3.0 & -\inf & +\inf & NaN +\end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} +NaN & -8.0 & 1.0 & 1.0 & +\inf & -\inf & 0.0 & 0.0 & NaN +\end{bmatrix} +``` + + +### Example 4 + +```math +A = \begin{bmatrix} +NaN & +1.0 & -1.0 & -\inf & -\inf & +\inf & 0.5 & 2.0 & -0.0 +\end{bmatrix} +\\ +B = \begin{bmatrix} +2.0 & -\inf & +\inf & 3.0 & -2.0 & -1.0 & -\inf & +\inf & 3.0 +\end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} +NaN & 1.0 & 1.0 & -\inf & +0.0 & +0.0 & +\inf & +\inf & -0.0 +\end{bmatrix} +``` + + +### Example 5 +This example illustrates the potential consequence of rounding on **pow**. + +```math +A = \begin{bmatrix} +-8.0 & -8.0 +\end{bmatrix} +\\ +B = \begin{bmatrix} +2.0 & 2.00000024 +\end{bmatrix} +``` + +```math +C \approx \begin{bmatrix} +64.00000000 & Nan +\end{bmatrix} +``` + + + + + + +## Error conditions + +No error condition in more than the definition. See $\text{NaN}$. + +## Attributes + +Operator **Pow** has no attribute. + +## Inputs + +### $\text{A}$: floating-point tensor + +Base of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $A$, $B$, and $C$ must have the same shape. +- `[C2]` Type consistency + + - Statement: Tensors $A$, $B$, and $C$ must have the same floating-point type. + +### $\text{B}$: floating-point tensor + +Exponent of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1fa_pow) on tensor $A$. +- `[C2]` Type consistency + + - Statement: see constraint [[C2]](#C2fa_pow) on tensor $A$. + +## Outputs + +### $\text{C}$: floating-point tensor + +Result of the element-wise power of $A$ to $B$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1fa_pow) on tensor $A$. +- `[C2]` Type consistency + + - Statement: see constraint [[C2]](#C2fa_pow) on tensor $A$. + +## Numeric accuracy + +[See the numeric accuracy note](./pow_acc.md). + + + + +# **Pow** (int, int) + +where int is in {int32, int64} + +## Signature + +Definition of operator $\text{Pow}$ signature: +$C = \textbf{Pow}(A, B)$ + +where: + +- $A$: base tensor +- $B$: exponent tensor +- $C$: result of element-wise power of $A$ to $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Pow** operator. + +## Informal specification + +Operator **Pow** computes the element-wise integer power between input tensors $A$ and $B$ and stores the result in output tensor $C$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +**pow** is undefined for $B[i] \lt 0$ +otherwise +$$ +C[i] =A[i]^{B[i]} +$$ + +The result is the exact integer power, provided no overflow occurs. Overflow behavior is implementation dependent. + +### Example 1 + +```math +A = \begin{bmatrix} 2 & 3 & 4 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 2 & 1 \end{bmatrix} +``` + +```math +C = \begin{bmatrix} 8 & 9 & 4 \end{bmatrix} +``` + +### Example 2 + +```math +A = \begin{bmatrix} 5 & 2 \\ 3 & 4 \end{bmatrix} +\quad +B = \begin{bmatrix} 0 & 3 \\ 2 & 1 \end{bmatrix} +``` + +```math +C = \begin{bmatrix} 1 & 8 \\ 9 & 4 \end{bmatrix} +``` + +## Error conditions + +The behaviour is implementation dependent if: + +- An exponent is negative. +- An overflow occurs during the computation. + +## Attributes + +Operator **Pow** has no attribute. + +## Inputs + +### $\text{A}$: integer tensor + +Base of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $A$, $B$, and $C$ must have the same shape. +- `[C2]` Type consistency + + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: integer tensor + +Exponent of the power operation. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1ia_pow) on tensor $A$. +- `[C2]` Type consistency + + - Statement: see constraint [[C2]](#C2ia_pow) on tensor $A$. +- `[C3]` Definition domain + + - Statement: for all index $i$, $B[i] \ge 0$. + +## Outputs + +### $\text{C}$: integer tensor + +Result of the element-wise power of $A$ to $B$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [[C1]](#C1ia_pow) on tensor $A$. +- `[C2]` Type consistency + + - Statement: see constraint [[C2]](#C2ia_pow) on tensor $A$. + diff --git a/safety-related-profile/sonnx/ops/spec/informal/pow/pow_acc.md b/safety-related-profile/sonnx/ops/spec/informal/pow/pow_acc.md new file mode 100644 index 00000000..a5eaa522 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/pow/pow_acc.md @@ -0,0 +1,68 @@ +## Numerical Accuracy + +$C_{\textit{err}} = C_{\textit{err}}^{\textit{propag}} + C_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $C_{\textit{err}}^{\textit{propag}}$, the propagated error, where $C$ is the tensor result of the **Pow** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $A_{\textit{err}}$, $B_{\textit{err}}$). For $C = A^B$, the propagated error $C_{\textit{err}}^{\textit{propag}}$ comes from the input errors $A_{\textit{err}}$ and $B_{\textit{err}}$. + +Using the first-order derivative of (A^B) with respect to (A) and (B): + +$$ +\frac{\partial (A^B)}{\partial A} = B \cdot A^{B-1}, \quad +\frac{\partial (A^B)}{\partial B} = A^B \cdot \ln(A) +$$ + +a first-order bound is: + +- For every index $I$ such that $A[I] > 0$ and $A[I] + A_{\textit{err}}[I] \ge 0$ (no crossing of singularities): + + - $|C_{\textit{err}}^{\textit{propag}}[I]| \le |B[I] \cdot A[I]^{B[I]-1} \cdot A_{\textit{err}}[I]| + |A[I]^{B[I]} \cdot \ln(A[I]) \cdot B_{\textit{err}}[I]|$ + +- If $A[I] \le 0$ or approaches zero, the bound may become very large or undefined, as ($\ln(A[I])$) or ($A[I]^{B[I]-1}$) may be undefined. + +- For integer exponents $B[I]$, the term $\ln(A[I]) \cdot B_{\textit{err}}[I]$ is replaced by exact integer power propagation rules. + +### Error Introduction +Error introduction for real (ideal) arithmetic is null: + +- $C_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor A, B; + +/* A and B symbolic initialization */ + +auto result = [&A, &B](auto I) { + // Real-domain pow, undefined for negative base with non-integer exponent + return (A[I].real > 0 || (A[I].real < 0 && std::floor(B[I].real) == B[I].real)) + ? pow(A[I], B[I]) + : SymbolicDomainError::undef(); +}; + +for (auto I : A.indexes()) { + auto a = A[I]; + auto b = B[I]; + + // Ensure we stay in the domain of pow under perturbation + if (a.real > 0 && a.real + a.err >= 0) { + auto y = result(I); + + // First-order propagated error bound + double bound_A = std::abs(b.real * std::pow(a.real, b.real - 1.0) * a.err); + double bound_B = std::abs(std::pow(a.real, b.real) * std::log(a.real) * b.err); + + assert(std::abs(y.err) <= bound_A + bound_B + 1e-12); + } +} +``` diff --git a/safety-related-profile/sonnx/ops/spec/informal/pow/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/pow/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/range/range.md b/safety-related-profile/sonnx/ops/spec/informal/range/range.md new file mode 100644 index 00000000..6dd70749 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/range/range.md @@ -0,0 +1,249 @@ +# Contents + +- **Range** operator for type [real](#real) +- **Range** operator for type [FP64, FP32, INT16, INT32, INT64](#types) + +Based on ONNX documentation version 11. + + +# **Range** (real, real, real) + +## Signature +$Y = \text{Range}(S, L, D)$ + +where: +- $S$: initial position of the range (**scalar** tensor) +- $L$: limit (exclusive) position of the range (**scalar** tensor) +- $D$: delta or step size (**scalar** tensor) + +## Restrictions +The following restrictions apply to the $\text{Range}$ operator for the SONNX profile: + +[General Restrictions](../general_restrictions.md) are applicable + +## Informal specification + +Operator $\text{Range}$ creates a sequence of numbers that begins at the specified $S$ value and extends by increments of $D$ up to, but not including, the specified $L$ value. + +$$ Y[i] = S + i \cdot D $$ + +Where: +- $K = \displaystyle \max \left( \ \left\lceil \frac{L - S}{D} \right\rceil , 0 \right)$ + +- $i \in [0, K - 1]$ + +\ +Note that if $D$ is positive and $S$ is greater than or equal to $L$, or if $D$ is negative and $S$ is less than or equal to $L$, the output tensor $Y$ will be empty. + +Moreover, whenever the interval $[0, K - 1]$ is empty (for instance when $K \leq 0$), the output tensor $Y$ will also be empty. + + +### Example 1 + +```math +S = 0 \quad L = 10 \quad D = 1 +``` + +```math +Y = \begin{bmatrix} 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \end{bmatrix} +``` + +### Example 2 + +```math +S = 10 \quad L = 2 \quad D = -3 +``` + +```math +Y = \begin{bmatrix} 10 & 7 & 4 \end{bmatrix} +``` + +### Example 3 + +```math +S = 10 \quad L = 10 \quad D = -3 +``` + +```math +Y = \begin{bmatrix} \end{bmatrix} +``` + +### Example 4 + +```math +S = 30 \quad L = 10 \quad D = 3 +``` + +```math +Y = \begin{bmatrix} \end{bmatrix} +``` + + +## Error conditions +No error condition. + +## Inputs + +### $S$: `real tensor` +Tensor $S$ is the first entry for the range of output values. + +Tensor $S$ is a scalar real tensor. + +### Constraints +Tensor $S$ does not have any specific constraints. + +### $L$: `real tensor` +Tensor $L$ is the exclusive upper limit for the range of output values. + +Tensor $L$ is a scalar real tensor. + +### Constraints +Tensor $L$ does not have any specific constraints. + +### $D$: `real tensor` +Tensor $D$ is the delta or step size of the Range. + +Tensor $D$ is a scalar real tensor. + +### Constraints + - `[C1]` Non-zero step size + - Statement: $D \neq 0$ + + - Rationale: Ensures that the step size is not zero to avoid infinite loops. + +## Outputs + +### $Y$: `real tensor` +Tensor $Y$ is a 1D tensor containing all values that lie in the range and are spaced by $D$. + +### Constraints +Tensor $Y$ does not have any specific constraints. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy +(-- IGNORE --) + + + +# **Range** (type, type, type) +Where type in in { FP64, FP32, INT64, INT32, INT16 } + +## Signature +$Y = \text{Range}(S, L, D)$ + +where: +- $S$: initial position of the range (**scalar** tensor) +- $L$: limit (exclusive) position of the range (**scalar** tensor) +- $D$: delta or step size (**scalar** tensor) + +## Restrictions +The following restrictions apply to the $\text{Range}$ operator for the SONNX profile: + +[General Restrictions](../general_restrictions.md) are applicable + +## Informal specification + +Operator $\text{Range}$ creates a sequence of numbers that begins at the specified $S$ value and extends by increments of $D$ up to, but not including, the specified $L$ value. + +$$ Y[i] = S + i \cdot D $$ + +Where: +- $K = \displaystyle \max \left( \ \left\lceil \frac{L - S}{D} \right\rceil , 0 \right)$ + +- $i \in [0, K - 1]$ + +\ +Note that if $D$ is positive and $S$ is greater than or equal to $L$, or if $D$ is negative and $S$ is less than or equal to $L$, the output tensor $Y$ will be empty. + +Moreover, whenever the interval $[0, K - 1]$ is empty (for instance when $K \leq 0$), the output tensor $Y$ will also be empty. + + +### Example 1 + +```math +S = 0 \quad L = 10 \quad D = 1 +``` + +```math +Y = \begin{bmatrix} 0 & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 \end{bmatrix} +``` + +### Example 2 + +```math +S = 10 \quad L = 2 \quad D = -3 +``` + +```math +Y = \begin{bmatrix} 10 & 7 & 4 \end{bmatrix} +``` + +### Example 3 + +```math +S = 10 \quad L = 10 \quad D = -3 +``` + +```math +Y = \begin{bmatrix} \end{bmatrix} +``` + +### Example 4 + +```math +S = 30 \quad L = 10 \quad D = 3 +``` + +```math +Y = \begin{bmatrix} \end{bmatrix} +``` + +## Error conditions +No error condition. + +## Inputs + +### $S$: `type tensor` +Tensor $S$ is the first entry for the range of output values. + +Tensor $S$ is a scalar type tensor. + +### Constraints +Tensor $S$ does not have any specific constraints. + +### $L$: `type tensor` +Tensor $L$ is the exclusive upper limit for the range of output values. + +Tensor $L$ is a scalar type tensor. + +### Constraints +Tensor $L$ does not have any specific constraints. + +### $D$: `type tensor` +Tensor $D$ is the delta or step size of the Range. + +Tensor $D$ is a scalar type tensor. + +### Constraints + - `[C1]` Non-zero step size + - Statement: $D \neq 0$ + + - Rationale: Ensures that the step size is not zero to avoid infinite loops. + +## Outputs + +### $Y$: `type tensor` +Tensor $Y$ is a 1D tensor containing all values that lie in the range and are spaced by $D$. + +### Constraints +Tensor $Y$ does not have any specific constraints. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy +(-- IGNORE --) diff --git a/safety-related-profile/sonnx/ops/spec/informal/range/reviews/doubts.md b/safety-related-profile/sonnx/ops/spec/informal/range/reviews/doubts.md new file mode 100644 index 00000000..eca94e58 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/range/reviews/doubts.md @@ -0,0 +1,185 @@ +# Doubts and Questions + +## Open Questions + +### Question 1: [Mismatch between expected shape and ONNX actual output shape] + +As addresed in the previous meeting, during our hypothesis testing we were unable to check that the expected shape matched the actual output shape from ONNX Range operator. + +The expected shape is calculated according to what is stated by the ONNX reference documentation that can be consulted here (https://onnx.ai/onnx/operators/onnx__Range.html#l-onnx-doc-range). + +$$ \text{expShape} = \displaystyle\ \max \left( \displaystyle\ \left\lceil \frac{\text{L} - \text{S}}{\text{D}}\right\rceil ,\ 0 \right) $$ + +Where: +- $\text{expShape}$ is the **expected** output shape + +- $\text{S}$ is the **start** of the range (inclusive) + +- $\text{L}$ is the **limit** of the range (exclusive) + +- $\text{D}$ is the **delta/step** of the range + +**Context:** +Based on the above formula and on the fact that $S$, $L$ and $D$ all have the same datatype, we implemented the following code to calculate the expected shape: + +```python +max( math.ceil( (l - s) / d ), 0 ) +``` + +The error mentioned above was only verified while using floating point datatypes (FLOAT32). + +**Example 1:** +``` +S: 0.5 +S_type: float32 +binary of S 00111111000000000000000000000000 + +L: -8388608.0 +L_type: float32 +binary of L 11001011000000000000000000000000 + +D: -1.0 +D_type: float32 +binary of D 10111111100000000000000000000000 + +Value of (L - S) / D: 8388608.5 +binary of (L - S) / D 01001011000000000000000000000000 + +Value after Ceil: 8388609 +binary after Ceil 01001011000000000000000000000001 + +Expected y shape: 8388609 +Actual y shape (ONNX): (8388608,) + +ONNX result: y: [ 5.0000000e-01 -5.0000000e-01 -1.5000000e+00 ... -8.3886045e+06 -8.3886055e+06 -8.3886065e+06] +``` + +**Example 2:** +``` +S: 0.5 +S_type: float32 +binary of S 00111111000000000000000000000000 + +L: -8388608.0 +L_type: float32 +binary of L 11001011000000000000000000000000 + +D: -16.0 +D_type: float32 +binary of D 11000001100000000000000000000000 + +Value of (L - S) / D: 524288.03125 +binary of (L - S) / D 01001001000000000000000000000000 + +Value after Ceil: 524289 +binary after Ceil 01001001000000000000000000010000 + +Expected y shape: 524289 +Actual y shape (ONNX): (524288,) + +ONNX result: y: [ 5.0000000e-01 -1.5500000e+01 -3.1500000e+01 ... -8.3885595e+06 -8.3885755e+06 -8.3885915e+06] + +``` + +**Example 3:** +``` +S: 9.999999747378752e-06 +S_type: float32 +binary of S 00110111001001111100010110101100 + +L: -256.0 +L_type: float32 +binary of L 11000011100000000000000000000000 + +D: -1.0 +D_type: float32 +binary of D 10111111100000000000000000000000 + +Value of (L - S) / D: 256.00000999999975 +binary of (L - S) / D 01000011100000000000000000000000 + +Value after Ceil: 257 +binary after Ceil 01000011100000001000000000000000 + +Expected y shape: 257 +Actual y shape (ONNX): (256,) + +ONNX result: y: [ 9.9999997e-06 -3.1999990e+01 -6.3999992e+01 -9.5999992e+01 -1.2799999e+02 -1.6000000e+02 -1.9200000e+02 -2.2400000e+02] +``` + +**Example 4:** +``` +S: 5.960464477539063e-08 +S_type: float32 +binary of S 00110011100000000000000000000000 + +L: -2.0 +L_type: float32 +binary of L 11000000000000000000000000000000 + +D: -2.0 +D_type: float32 +binary of D 11000000000000000000000000000000 + +Value of (L - S) / D: 1.0000000298023224 +binary of (L - S) / D 00111111100000000000000000000000 + +Value after Ceil: 2 +binary after Ceil 01000000000000000000000000000000 + +Expected y shape: 2 +Actual y shape (ONNX): (1,) + +ONNX result: y: [5.9604645e-08] +``` + +\ +These are some of the examples we obtained for which the expected shape does not match the actual output shape from ONNX Range operator. + +We haven't yet analysed the root cause of this mismatch, but we suspect it might be related to floating point precision errors. We plan to do **Ceil** operator in the coming days and after that we might be able to investigate this issue further. + +One possible cause of this problem might be that the calculation of the expected shape uses higher precision arithmetic than the one used by ONNX Range operator internally, which could lead to small discrepancies in the final result of the division and consequently in the Ceil operation. This is just a hypothesis that we need to investigate further, but once python uses, by default , double precision (float64) this could be the root of the problem. + +Here is a quick analysis of **Example 4** to illustrate our suspicion. Please baer in mind that all genertaed variables ($S$, $L$, $D$) are of type FLOAT32. We know that because we set them explicitly to be generated as FLOAT32 values in Hypothesis. + +![Float32 precision analysis for Example 4](./imgs/img1.png) + + +We did not find a way to identify the exact precision of $\text{dif}$ and $\text{div}$ variables. However it seems possible that they are calculated using higher precision arithmetic (e.g., FLOAT64) which would explain the mismatch in the final result. Please note that our expected shape match indeed the shape proposed with higher precision arithmetic while the ONNX actaul shape matches the one obtained with the converted \{div} to a numpy FLOAT32 value. + +### Question 2: [Overflow in the calculation of expected shape] + +Moreover, we also found some cases where the calculation of the expected shape overflows while the ONNX Range operator is still able to provide a valid output. + +**Example 5:** +``` +S: 0.0 +S_type: float64 + +L: 8.98846567431158e+307 +L_type: float64 + +D: 0.5 +D_type: float64 +``` + +**ONNX Execution** +``` +S shape: (), +X=0.00000000 + +L shape: (), +X=8.98846567e+307 + +D shape: (), +X=0.50000000 + +Output shape: (0,), +Result = [] +``` + +At first glance, ONNX seems to produce a strange result. Note that for the range defined, the output tensor shouldn't be empty at all. + +We did not find a smart way to deal with this overflow problem yet. + +Until now our solution is to limit the range of the input generated values ( $\text{S}, \text{L}, \text{D}$ ) to avoid this overflow situation. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/range/reviews/imgs/img1.png b/safety-related-profile/sonnx/ops/spec/informal/range/reviews/imgs/img1.png new file mode 100644 index 00000000..557bb0ba Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/range/reviews/imgs/img1.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/relu/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/relu/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/relu/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/relu/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/relu/relu.md b/safety-related-profile/sonnx/ops/spec/informal/relu/relu.md new file mode 100644 index 00000000..7b88574b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/relu/relu.md @@ -0,0 +1,222 @@ +# Contents + +- **Relu** operator for type [real](#real) +- **Relu** operator for types [float16, float, double](#float) +- **Relu** operator for types [int8, int16, int32, int64](#int) + +Based on ONNX documentation version 14. + + +# **Relu** (real) + +## Signature +$Y = \text{Relu}(X)$ + +where: +- $X$: input tensor +- $Y$: result of the element-wise application of **Relu** on $X$ + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) : GR1 and GR2 are applicable. + +No specific restrictions apply to the **Relu** operator. + +## Informal specification + +Operator **Relu** is defined as follows: +- if $X[i] < 0$ then $Y[i] = 0$ +- If $X[i] \ge 0$ then $Y[i]=X[i]$ + +### Example 1 + +```math +X = \begin{bmatrix} 6.1 & -9.5 & 35.7 \end{bmatrix} +``` + +```math +Y = \text{Relu}(X) = \begin{bmatrix} 6.1 & 0 & 35.7 \end{bmatrix} +``` + +## Error conditions +No error condition. + +## Inputs + +### $\text{X}$: `real tensor` +Argument of the **Relu**. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +## Outputs + +### $\text{Y}$: `real tensor` + +Tensor $Y$ is the output of the **Relu** applied to $X$. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Attributes + +Operator **Relu** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **Relu** does not introduce any numerical error. + + + +# **Relu** (float) +where float is in {float16, float, double} + +## Signature +$Y = \text{Relu}(X)$ + +where: +- $X$: input tensor +- $Y$: result of the element-wise application of **Relu** on $X$ + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) : GR1 and GR2 are applicable. + +No specific restrictions apply to the **Relu** operator. + +## Informal specification + +Operator **Relu** is defined as follows: +- if $X[i] = \text{NaN}$ then $Y[i]=\text{NaN}$ +- if $X[i] < 0$ then $Y[i] = 0$ +- If $X[i] \ge 0$ then $Y[i]=X[i]$ + +### Example 1 + +```math +X = \begin{bmatrix} 6.1 & -9.5 & 35.7 \end{bmatrix} +``` + + +```math +Y = \text{Relu}(X) = \begin{bmatrix} 6.1 & 0 & 35.7 \end{bmatrix} +``` + +## Error conditions +No error condition. + +## Inputs + +### $\text{X}$: `floating-point tensor` +Argument of the **Relu**. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +## Outputs + +### $\text{Y}$: `floating-point tensor` + +Tensor $Y$ is the output of the **Relu** applied to $X$. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ra) on tensor $X$. + +## Attributes + +Operator **Relu** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **Relu** does not introduce any numerical error. + + + + + +# **Relu** (int) +where int is {int8, int16, int32, int64} + +## Signature +$Y = \text{Relu}(X)$ + +where: +- $X$: input tensor +- $Y$: result of the element-wise application of **Relu** on $X$ + +## Restrictions + +[General restrictions](/working-groups/safety-related-profile/sonnx/ops/spec/informal/common/general_restrictions.md) : GR1 and GR2 are applicable. + +No specific restrictions apply to the **Relu** operator. + +## Informal specification + +Operator **Relu** is defined as follows: +- if $X[i] < 0$ then $Y[i] = 0$ +- If $X[i] \ge 0$ then $Y[i]=X[i]$ + +### Example 1 + +```math +X = \begin{bmatrix} 6 & -9 & 35 \end{bmatrix} +``` + + +```math +Y = \text{Relu}(X) = \begin{bmatrix} 6 & 0 & 35 \end{bmatrix} +``` + +## Error conditions + +No particular error, the function returns $\text{NaN}$ when the input is $\text{NaN}$. + +## Inputs + +### $\text{X}$: `integer tensor` +Argument of the **Relu**. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: Tensors $X$ and $Y$ must have the same shape. + +## Outputs + +### $\text{Y}$: `integer tensor` + +Tensor $Y$ is the output of the **Relu** applied to $X$. + +#### Constraints + + - `[C1]` Shape consistency + - Statement: see constraint [[C1]](#C1ia) on tensor $X$. + +## Attributes + +Operator **Relu** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **Relu** does not introduce any numerical error. + diff --git a/safety-related-profile/sonnx/ops/spec/informal/relu/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/relu/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/relu/reviews/jean.md b/safety-related-profile/sonnx/ops/spec/informal/relu/reviews/jean.md new file mode 100644 index 00000000..0f452ae9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/relu/reviews/jean.md @@ -0,0 +1,11 @@ +Rmk1 : Typos and reformatting, already taken into account in relu.md + +Rmk2: In ONNX, the name of the operator is **R**elu, not relu. Taken into account in relu.md + +Rmk3: Replace type names FPnn, INTnn and UINTnn by their ONNX counterpart. Example: replace UINT8 by uint8. Taken into account in relu.md + +Rmk4: ONNX bfloat16 type is in the ONNX description of relu but not in SONNX relu.md. If this type is never used in SONNX, it has to be inserted as a general restriction. + +Rmk5: broken links. Taken into account in relu.md. + +Rmk6: Replace max by Max in the three occurrences of "Operator **Relu** computes: $Y = \text{max}(0, X)$ where $\text{max}$ is ONNX **max** operator.". Taken into account in relu.md. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/reshape/reshape.md b/safety-related-profile/sonnx/ops/spec/informal/reshape/reshape.md new file mode 100644 index 00000000..b51e8e6e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/reshape/reshape.md @@ -0,0 +1,286 @@ +# Contents +- **Reshape** operator for type [real](#real) +- **Reshape** operator for type [bool, string, float16, float32, float64, int2, int4, int8, int16, int32, int64, uint2, uint4, uint8, uint16, uint32, uint64](#types) + +Based on ONNX [Op version 25](https://onnx.ai/onnx/operators/onnx__Reshape.html). + +# **Reshape** + +# Signature +$Y = \textbf{Reshape}(X, S)$ + +where: +- $data$: input tensor (denoted by $X$) +- $shape$: desired shape (denoted by $S$) + +## Restrictions +[General Restrictions](../general_restrictions.md) are applicable + +## Informal specification + +Operator **Reshape** transforms the input tensor into an output tensor with the shape specified in $S$. + +The reshape operation depends on the attribute `allowzero`. If: + +- `allowzero = 0`: Any dimension **equal to $0$** in $S$ means copying the respective dimension from the input tensor $X$. + +- `allowzero = 1`: Any dimension **equal to $0$** in $S$ means that the respective dimension in the output tensor $Y$ should be null. + +It is possible to have **at most one dimension in $S$ with value -1**. In such case that dimension is inferred from the size of the input tensor $X$ and the remaining dimensions in $S$. + +The operation is performed in two steps. + +### 1. Compute a the flattened index for the output tensor $Y$: + +$$\mathit{flat\_y} = \operatorname{offset}\!\left(\,Y_{\mathit{coords}},\; Y_{\mathit{shape}}\,\right)$$ + +where: +- $Y_{\text {coords}}$ are the coordinates of an element in the output tensor $Y$. +- $Y_{\text {shape}}$ is the shape of the output tensor $Y$. +- $\text{offset}$ is defined [here](../common/definitions). + +### 2. Compute the corresponding $X$ coordinates from the flattened index: +$$X_{\mathit{coords}} = index \left(\mathit{flat\_y}, X_{\mathit{shape}}\right)$$ + +where: +- $flat\_y$ is the flattened index computed in step 1. + +- $X_shape$ is the shape of the input tensor $X$. + +- $index$ is defined [here](../common/definitions). + +--- + +Reshape operation can then be expressed as: + +$$Y[i_0, i_1, \ldots, i_{rY-1}] = X[\displaystyle index \left(offset([i_0, i_1, \ldots, i_{rY-1}], Y_{\mathit{shape}}), X_{\mathit{shape}}\right)] $$ + +Where: +- $i_n \in [0, dY_n - 1]$ + +- $X_{\mathit{shape}}$ is the shape of the input tensor $X$ + +- $Y_{\mathit{shape}}$ is the shape of the output tensor $Y$ + + +### Example 1 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 & 4 \end{bmatrix} + \begin{bmatrix} 5 & 6 & 7 & 8 & 9 \end{bmatrix} + \end{bmatrix} +``` + +```math +allowzero = 0 +``` + +```math +\text{S} = [5, 2] +``` + +```math +Y_\mathit{shape} = [5, 2] +``` + +```math +Y = \begin{bmatrix} + \begin{bmatrix} + 0 & 1 \\ 2 & 3 \\ 4 & 5 \\ 6 & 7 \\ 8 & 9 + \end{bmatrix} + \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +allowzero = 0 +``` + +```math +\text{S} = [0, 6, -1] +``` + +```math +Y_\mathit{shape} = [2, 6, 2] +``` + +```math +Y = \begin{bmatrix} + \begin{bmatrix} + 0 & 1 \\ + 2 & 3 \\ + 4 & 5 \\ + 6 & 7 \\ + 8 & 9 \\ + 10 & 11 + \end{bmatrix} + \begin{bmatrix} + 12 & 13 \\ + 14 & 15 \\ + 16 & 17 \\ + 18 & 19 \\ + 20 & 21 \\ + 22 & 23 + \end{bmatrix} + \end{bmatrix} +``` + +### Example 3 + +```math +X = \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 & 4 \\ + 5 & 6 & 7 & 8 & 9 + \end{bmatrix} + \end{bmatrix} +``` + +```math +allowzero = 1 +``` + +```math +\text{S} = [0, -1] +``` + +```math +Y_\mathit{shape} = [2, 5] +``` + +```math +Y = \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 & 4 \\ + 5 & 6 & 7 & 8 & 9 + \end{bmatrix} + \end{bmatrix} + +``` +### Example 4 + +```math +X = \begin{bmatrix} + \ + \end{bmatrix} +``` + +```math +X_\text{shape} = [0, 3, 8] +``` + +```math +allowzero = 1 +``` + +```math +\text{S} = [1, 2, 0] +``` + +```math +Y_\mathit{shape} = [1, 2, 0] +``` + +```math +Y = \begin{bmatrix} + \ + \end{bmatrix} +``` + +## Error conditions + +No error conditions. + +## Attributes + +### allowzero: integer +If set to 0, dimensions in $S$ with value 0 will copy the size from the corresponding dimension of the input tensor $X$. + +If set to 1, any 0 in $S$ means that the size of that dimension should be 0. + +For any value other than 0 in $S$, the respective value will be kept (regardless of the value of `allowzero`). + + +#### Constraints + + - `[C1]` Value domain + - Statement: + - $\text{allowzero} \in \{0, 1\} $ + + - `[C2]` Dimension size copying from input tensor $X$ + - Statement: With `allowzero = 0` only valid dimensions can be copied from the input tensor $X$: + - $\forall i \in [0, dS - 1]. \quad S[i] = 0 \implies i < rX$ + + - Rationale: Only valid dimensions in $X$ can be copied to the output. + + - `[C3]` Value domain in $S$ + - Statement: + - If `allowzero = 0`: + + $$\forall i \in [0, dS_0 - 1] . \quad S[i] >= -1 \quad \land \quad $$ + $$(\exists j \in [0, dS_0 - 1]. \quad S[j] = -1) \implies (\forall d \in [0, rX - 1]. \quad dX_d \neq 0)$$ +
+ + - If `allowzero = 1`: + + $$\forall i \in [0, dS_0 - 1]. \quad S[i] > 0 \quad \lor\\$$ + $$(S[i] = 0 \implies ((\exists d \in [0, rX - 1]. \quad dX_d = 0) \quad \land \quad (\forall z \in [0, dS_0 - 1]. \quad S[z] \neq -1) )) \quad \lor\\$$ + $$(S[i] = -1 \quad \implies (\forall z \in [0, dS_0 - 1]. \quad S[z] \neq 0))$$ + + - Rationale: + - `allowzero = 0`: Auto inference is not compatible with null tensors (division by 0). + + - `allowzero = 1`: Dimensions equal to `0` or `1` are mutually incompatible. Null tensors are only feasible ate output if the input is also null. + +## Inputs + +### $X$: real +Tensor $X$ is the input tensor to be reshaped. + +### Constraints + + - `[C1]` Consistency between the shapes of tensor $X$ and the output tensor $Y$. + - Statement: $size_X = size_Y$ + + where: + - $size$ is defined [here](../common/definitions.md) + +### $S$: int64 tensor +Tensor $S$ is a 1D tensor that specifies the desired shape of the output tensor $Y$. + +### Constraints + - `[C1]` Automatic shape inference + - Statement: + - $\forall i,j \in [0, dS_0 - 1]. \quad S[i] = -1 \land S[j] = -1 \implies i = j$ + - Rationale: Ensures that the at most one dimenson can be automatically inferred. + + - `[C2]` Dimension size copying from input tensor $X$ + - Statement: see constraint [[C2]](#C1ra) on attribute $\text{allowzero}$. + + - `[C3]` Value domain + - Statement: see constraint [[C3]](#C2ra) on attribute $\text{allowzero}$. + + +## Outputs + +### $Y$: real tensor +Tensor $Y$ is the reshaped output tensor. + +### Constraints + - `[C1]` Shape consistency + - Statement: See constraint [[C1]](#C3ra) on tensor $X$. + +## Formal specification +See the Why3 specification. + +## Numerical Accuracy +The $\text{Reshape}$ operator does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/shape/shape.md b/safety-related-profile/sonnx/ops/spec/informal/shape/shape.md new file mode 100644 index 00000000..f0c6f094 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/shape/shape.md @@ -0,0 +1,399 @@ +# Contents + +- **Shape** operator for type [real](#real) +- **Shape** operator for type [ BFLOAT16, FP64, FP32, FP16, INT2, INT4, INT8, INT16, INT32, INT64, UINT2, UINT4, UINT8, UINT16, UINT32, UINT64, STRING, BOOL](#types) + +Based on ONNX documentation version 25. + + +# **Shape** (real) + +## Signature +$Y = \text{Shape}(X)$ + +where: +- $X$: input tensor +- $Y$: output tensor (1D Tensor) + +## Restrictions +The following restrictions apply to the $\text{Shape}$ operator for the SONNX profile: + +[General Restrictions](../general_restrictions.md) are applicable + +## Informal specification +The $\text{Shape}$ operator takes a tensor $X$ as input and produces a 1D tensor $Y$ containing the sliced shape of $X$ according to the `start` and `end` attributes. + +Shape operation can be divided into two steps: + +### 1. Clamping of `start` and `end` attributes +The `start` and `end` attributes are clamped based on the rank of the input tensor $X$: + +#### Start +$$ start < 0 \implies start = start + rX $$ + +If the clamped `start` is still less than 0, it is set to 0: + +$$ start' < 0 \implies start' = 0 $$ + +Where: +- $start'$ is the clamped value of `start` + +#### End +$$ end < 0 \implies end = end + rX $$ + +If `end` is greater than the rank of $X$, it is set to the rank of $X$: + +$$ end > rX \implies end = rX $$ + +### 2. Slicing the shape of input tensor +The output tensor $Y$ is obtained by slicing the shape of the input tensor $X$ from index `start` to index `end` (exclusive): + + +$$ \forall i \in [start', end'[. \quad Y[i - start'] = S[i] $$ + +Where: +- $S$ is the shape of input tensor $X$ +- $start'$ is the clamped value of `start` +- $end'$ is the clamped value of `end` + +Note that if `start` is greater than or equal to `end`, the output tensor $Y$ will be an empty tensor. + +### Example 1 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 0 \quad start' = 0 +``` +```math +end = 3 \quad end' = 3 +``` +```math +Y = [2, 3, 4] +``` + +### Example 2 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 1 \quad start' = 1 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [3] +``` + +### Example 3 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 2 \quad start' = 2 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [] +``` + +### Example 4 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = -500 \quad start = -500 + 3 = -497 \quad start' = 0 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [2,3] +``` + + + +### Example 5 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 0 \quad start' = 0 +``` +```math +end = 1000 \quad end' = 3 +``` +```math +Y = [2, 3, 4] +``` + +## Error conditions +No error condition + + +## Inputs + +### $X$: `real tensor` +Tensor $X$ is the input tensor to extract the shape from. + +### Constraints +Tensor $X$ has no constraints. + +## Attributes + +### start: `integer` +Specifies the starting index of the slice. + +### Constraints +Attribute `start` has no constraints. + +### end: `integer` +Specifies the ending index of the slice (exclusive). +### Constraints +Attribute `end` has no constraints. + +## Output + +### $Y$: `integer tensor` +Tensor $Y$ is a 1D tensor containing the sliced shape of input tensor $X$. +### Constraints +Tensor $Y$ has no constraints. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +The $\text{Shape}$ operator does not introduce any numerical error. Hence, all valid indices of the output values belong to the shape of the input tensor. + + + +# **Shape** (type) + +Where type in { BFLOAT16, FP64, FP32, FP16, INT2, INT4, INT8, INT16, INT32, INT64, UINT2, UINT4, UINT8, UINT16, UINT32, UINT64, STRING, BOOL} + +## Signature +$Y = \text{Shape}(X)$ + +where: +- $X$: input tensor +- $Y$: output tensor (1D Tensor) + +## Restrictions +The following restrictions apply to the $\text{Shape}$ operator for the SONNX profile: + +[General Restrictions](../general_restrictions.md) are applicable + +## Informal specification +The $\text{Shape}$ operator takes a tensor $X$ as input and produces a 1D tensor $Y$ containing the sliced shape of $X$ according to the `start` and `end` attributes. + +Shape operation can be divided into two steps: + +### 1. Clamping of `start` and `end` attributes +The `start` and `end` attributes are clamped based on the rank of the input tensor $X$: + +#### Start +$$ start < 0 \implies start = start + rX $$ + +If the clamped `start` is still less than 0, it is set to 0: + +$$ start' < 0 \implies start' = 0 $$ + +Where: +- $start'$ is the clamped value of `start` + +#### End +$$ end < 0 \implies end = end + rX $$ + +If `end` is greater than the rank of $X$, it is set to the rank of $X$: + +$$ end > rX \implies end = rX $$ + +### 2. Slicing the shape of input tensor +The output tensor $Y$ is obtained by slicing the shape of the input tensor $X$ from index `start` to index `end` (exclusive): + + +$$ \forall i \in [start', end'[. \quad Y[i - start'] = S[i] $$ + +Where: +- $S$ is the shape of input tensor $X$ +- $start'$ is the clamped value of `start` +- $end'$ is the clamped value of `end` + +Note that if `start` is greater than or equal to `end`, the output tensor $Y$ will be an empty tensor. + +### Example 1 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 0 \quad start' = 0 +``` +```math +end = 3 \quad end' = 3 +``` +```math +Y = [2, 3, 4] +``` + +### Example 2 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 1 \quad start' = 1 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [3] +``` + +### Example 3 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 2 \quad start' = 2 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [] +``` + +### Example 4 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = -500 \quad start = -500 + 3 = -497 \quad start' = 0 +``` +```math +end = 2 \quad end' = 2 +``` +```math +Y = [2,3] +``` + + + +### Example 5 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +X.shape = [2, 3, 4] +``` +```math +start = 0 \quad start' = 0 +``` +```math +end = 1000 \quad end' = 3 +``` +```math +Y = [2, 3, 4] +``` + +## Error conditions +No error condition + + +## Inputs + +### $X$: `type tensor` +Tensor $X$ is the input tensor to extract the shape from. + +### Constraints +Tensor $X$ has no constraints. + +## Attributes + +### start: `integer` +Specifies the starting index of the slice. + +### Constraints +Attribute `start` has no constraints. + +### end: `integer` +Specifies the ending index of the slice (exclusive). +### Constraints +Attribute `end` has no constraints. + +## Output + +### $Y$: `integer64 tensor` +Tensor $Y$ is a 1D tensor containing the sliced shape of input tensor $X$. +### Constraints +Tensor $Y$ has no constraints. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +The $\text{Shape}$ operator does not introduce any numerical error. Hence, all valid indices of the output values belong to the shape of the input tensor. diff --git a/safety-related-profile/sonnx/ops/spec/informal/sigmoid/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sigmoid/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sigmoid/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid.md b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid.md new file mode 100644 index 00000000..b5d10d7b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid.md @@ -0,0 +1,222 @@ +# Contents + +- **Sigmoid** operator for type [real](#real) +- **Sigmoid** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Sigmoid version 13](https://onnx.ai/onnx/operators/onnx__Sigmoid.html). + + +# **Sigmoid** (real) + +## Signature +$Y = \textbf{Sigmoid}(X)$ + +where: +- $X$: Input tensor +- $Y$: Sigmoid of $X$ + + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sigmoid** operator. + +## Informal specification + +The **Sigmoid** operator computes the element-wise sigmoid of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = \frac{1}{1 + e^{-X[i]}} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0.5 & 0.73105860 & 0.26894143 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.11920291 & 0.5 \\ + 0.73105860 & 0.88079709 \\ + 0.01798624 & 0.98201376 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Sigmoid** has no attribute. + +## Inputs + +### $\text{X}$: real + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: real + +Sigmoid of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1rx). + +## Formal specification + +See the Why3 specification. + + +# **Sigmoid** (float) +where float is in {float16, float, double} + +## Signature + +$Y = \textbf{Sigmoid}(X)$ + +where: +- $X$: Input tensor +- $Y$: Sigmoid of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sigmoid** operator. + +## Informal specification + +The **Sigmoid** operator computes the element-wise logistic sigmoid of the input tensor $X$ according to IEEE 754 floating-point semantics. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + + +$$ +Y[i] = +\begin{cases} +\text{0.0} & \text{if } X[i]=\text{-inf} \\ +\text{1.0} & \text{if } X[i]=\text{inf} \\ +\text{NaN} & \text{if } X[i]=\text{NaN} \\ + +1 / (1 + e^{-X[i]}) & \text{otherwise} \\ +\end{cases} +$$ + + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0.5 & 0.73105860 & 0.26894143 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.11920291 & 0.5 \\ + 0.73105860 & 0.88079709 \\ + 0.01798624 & 0.98201376 +\end{bmatrix} +``` + + +### Example 3 + +```math +X = \begin{bmatrix} + \text{+inf} & \text{NaN} & \text{-inf} +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 1.0 & \text{NaN} & 0.0 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Sigmoid** has no attribute. + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: $X$ and $Y$ shall have the same floating-point type. + +## Outputs + +### $\text{Y}$: floating-point tensor + +Sigmoid of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1fx). +- `[C2]` Type consistency + - Statement: See [constraint (C2) on X](#C2fx). + +## Numeric accuracy + +[See the numeric accuracy note](./sigmoid_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid_acc.md b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid_acc.md new file mode 100644 index 00000000..e604bf92 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sigmoid/sigmoid_acc.md @@ -0,0 +1,74 @@ +## Note Algorithm +Sigmoid is subject to exponent overflow when evaluating large positive exponents (e.g. exp(-X) for very negative values of X). +To remain numerically stable in float, the minimal precision algorithm shall split the `X` domain so that only negative exponents are computed. + +``` +if X >= 0 + Y = 1 / (1 + exp(-X)) +else + Y = exp(X) / (1 + exp(X)) +``` + +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Sigmoid** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = \sigma(X)$ with $\sigma(x) = 1/(1+e^{-x})$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\sigma$ is $d\sigma(x)/dx = \sigma(x)(1-\sigma(x))$, a first-order bound is: + +- For every index $I$: + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |\sigma(X[I])(1-\sigma(X[I]))|\cdot|X_{\textit{err}}[I]|$ + +- Since $0 \le \sigma(x)\,(1-\sigma(x)) \le 1/4$ for all real $x$, a global bound is: + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le \frac{1}{4}|X_{\textit{err}}[I]|$ + +### Error Introduction +Error introduction for real (ideal) arithmetic is null: +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto sigmoid = [](const SymbolicDomainError &v) { + // Sigmoid in the real domain: sigma(x) = 1 / (1 + exp(-x)) + SymbolicDomainError r; + r.real = 1.0 / (1.0 + std::exp(-v.real)); + // float/err/rel_err are set by the abstract interpreter / analysis framework + return r; +}; + +auto result = [&X,&sigmoid](auto I) { + return sigmoid(X[I]); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + auto y = result(I); + + // First-order propagated error bound: + // |err_sigmoid| <= |sigma(x) * (1 - sigma(x))| * |err_x| + double sigma_x = 1.0 / (1.0 + std::exp(-x.real)); + double local_lipschitz = std::abs(sigma_x * (1.0 - sigma_x)); + double bound = local_lipschitz * std::abs(x.err); + + // Using the global bound 1/4 * |x.err| is also valid but less tight. + assert(std::abs(y.err) <= bound + 1e-12); +} +``` + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/example.png b/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/example.png new file mode 100644 index 00000000..20cb1e56 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/example.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/m_explanation.png b/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/m_explanation.png new file mode 100644 index 00000000..26857175 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/slice/assets/imgs/m_explanation.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/slice/reviews/joao-ricardo-doubts.md b/safety-related-profile/sonnx/ops/spec/informal/slice/reviews/joao-ricardo-doubts.md new file mode 100644 index 00000000..43c9d51a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/slice/reviews/joao-ricardo-doubts.md @@ -0,0 +1,206 @@ +# SONNX Slice Operator Review + +This document reviews key design considerations and potential issues related to the **slice** operator definition in the SONNX profile, highlighting areas that require clarification or decision-making. + +--- + +## 1. Axis arrangement + +### Current Issue + +One of the core SONNX restrictions prohibits default values. Consequently, the input tensor **A** (axes) must explicitly list **all axes** of the input tensor **X**. + +### Comparison: ONNX vs SONNX + +| Framework | Behavior | Example (X.shape = (8, 3, 6, 3, 2)) | +|-----------|----------|--------------------------------------| +| **ONNX** | Missing axes use default values | `A = [3, 0, 4]` | +| **SONNX** | All axes must be specified | `A = [3, 0, 4, 1, 2]` | + +> The only default value here is the case where A is empty. (In that case, all axes are taken). We only forbid that A is not given. + +### Proposed Simplification + +Since all axes must be represented, using an **ordered representation** would be both clearer and easier to define: + +``` +A = [0, 1, 2, 3, 4] // Sequential axis ordering +``` +> Give a less specific example, e.g., [1,3,4] + +> There is a risk that we won't be able to process existing models that may not comply with this constraint... So it is probably not a good idea to impose this restriction... + +**Benefits:** + + +- Eliminates ambiguity in axis ordering +> Why is it ambiguous? + +- Simplifies operator definition and verification +- Reduces potential for indexing errors +- Based on this structure: + - axis attribute wouldn't even be needed as an argument + - t axis wouldn't be needed to represent +> What it "t axis"? +--- + +## 2. Negative Indexing Support + +### Current Behavior + +The operator supports negative indexing for `A`, `S`, and `E` parameters, with preprocessing to normalize values: + +**For tensor X with:** +- **X.shape = (8, 3, 6, 3, 2)** + +- **$r = \text{rank}(X) = 5$** + +- **Axes range:** $A[i] \in [-r, r-1] = [-5, 4]$ + +- **Start range:** $S[i] \in [-d_j, d_j - 1]$ where $d_j$ is the dimension of axis $j$ + +- **End range:** $S[i], E[i] \in [-d_j - 1, d_j]$ where $d_j$ is the dimension of axis $j$ + +### Mathematical Normalization + +The normalization process converts negative indices to positive equivalents: +```math +\text{normalized\_index\_axis} = \begin{cases} +\text{index} & \text{if } \text{index} \geq 0 \\ +\text{index} + \text{rank} & \text{if } \text{index} < 0 +\end{cases} +``` +```math +\text{normalized\_index\_start/end} = \begin{cases} +\text{index} & \text{if } \text{index} \geq 0 \\ +\text{index} + \text{dimension of the respective index} & \text{if } \text{index} < 0 +\end{cases} +``` + +### Open Questions + +1. **Should SONNX prohibit negative indexing entirely?** + - Would simplify specification and verification + + - Reduces preprocessing complexity + + - More readable/understandable + + +> If you think that supporting negative values does not overcomplexifies the format spec + proof, please do it, otherwise we will stick to the initial restriction. + +--- + +## 3. Mixed Positive/Negative Indexing + +### Potential Ambiguity + +The current specification allows mixing positive and negative indices within the same operation. +However, based on this approach, we must guarantee that no index has multiple representations. + +**Example:** +``` +X = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] // length = 10 + +A = [-2, -1, 0, 3] // equivalent to A = [8, 9, 0, 3] after normalization +``` + +> See previous remark. It is up to you. You are right that having 2 different designations may be a problem, but it is also a feature. + +### Concerns + +- **Readability:** Mixed indexing can be confusing + +- **Verification complexity:** Requires additional normalization logic in formal specifications + +- **Error proneness:** Higher chance of off-by-one errors + +### Recommendation + +Consider restricting to **either** all positive **or** all negative indexing within a single operation. + +> That's probably a good idea, we will discuss it during next meeting. + +--- + +## 4. Output Dimension Constraints + +### Why3 Tensor Invariant + +The Löic tensor files in Why3 enforce the following invariant: + +> **Positive Dimensions:** All tensors must have positive dimensions. + +### Mathematical Constraint + +$$\forall i \in [0, \text{rank}(Y)-1] : \text{dim}(Y_i) > 0$$ + +> This is strange. You should probably get in touch with Loïc to address this issue. We also have to check is scalars are actually represented as null-rank tensors in ONNX (or as dimension 1x1x1x1x..X1 tensors). + + +### Implications + +This constraint would **prohibit** tensors with zero-dimensional axes: + +- **Valid:** `Y.shape = (5, 2, 3, 1)` + +- **Invalid:** `Y.shape = (5, 2, 0, 1)` + +### Impact on Slice Operations + +Zero-dimensional outputs can legitimately occur when: +- Empty slicing ranges: $\text{end} = \text{start}$ + +> As far as we understand, a tensor with a dimension with zero element is an empty tensor. So it seems to make sense and we have to take this into account. + +- Mathematical edge cases in dimension calculations + +--- + +### Decision Required + +**Question:** Should SONNX slice operations be required to guarantee non-zero output dimensions? + +> No, we have to take into account the case of tensors with null dimensions. + +**Options:** +1. **Enforce positive dimensions:** Add preconditions to prevent zero-dimensional outputs + +> No (see above) + +2. **Allow zero dimensions:** Modify Why3 tensor invariants to permit empty dimensions + +- Yes. + +--- + +## Summary of Open Doubts + +- Ordered axis representation or Axis attribute remotion + +> No. + +- Negative indexing support + +> Yes. + +- Mixed indexing restrictions + +> Yes. + +- Zero-dimensional output handling + +> Yes +--- + +## Operator status + +We have already made the informal spec, hypothesis tests and the formal spec (real tensors) made without any of this "additional" restrictions. + +> Perfect... + +We identified those and we want to know if any of these should be in fact considered restrictions. + +If so we will adjust our work accordingly. + +> No \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/slice/slice.md b/safety-related-profile/sonnx/ops/spec/informal/slice/slice.md new file mode 100644 index 00000000..f68ca0dd --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/slice/slice.md @@ -0,0 +1,581 @@ +# Contents + +- **slice** operator for type [real](#real) +- **slice** operator for types [INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, DOUBLE, BFLOAT16, BOOL, STRING](#types) + +Based on ONNX documentation version 13. + + +# **slice** (real) + +## Signature +$Y = \text{slice}(X,S,E,A,K)$ + +where: +- `X`: input tensor +- `A`: 1-D tensor of axes to slice +- `S`: 1-D tensor with starting indices of corresponding axis in `A` +- `E`: 1-D tensor with ending indices of corresponding axis in `A` +- `K`: 1-D tensor of steps for each axis in `A` +- `Y`: output tensor + + +## Restrictions +The following restrictions apply to the **slice** operator for the SONNX profile: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input `A` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | All axes must be specified for input `A` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Input `K` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R4]` | Sparse tensors are not supported | General restriction [GR1](../general_restrictions.md#GR1) | +| `[R5]` | Shape of tensors shall be explicit | General restriction [GR2](../general_restrictions.md#GR2) | +| `[R6]` | Positive steps must have starting positions lower or equal than ending positions | Transient | +| `[R7]` | Negative steps must have starting positions greater or equal than ending positions | Transient | +| `[R8]` | For all axis of the input tensor, the start value, on tensor `S`, that represent that axis must be lower than the axis dimension | Transient | + +## Informal specification + + +Operator **slice** extracts from `X` a subtensor defined by the axes listed in `A`. + +For each axis, `i` of the input tensor, slicing: +- starts at $S'[i]$ +- slides with a step of $K[i]$ +- stops strictly before $E'[i]$ + +The result is stored in output tensor `Y`. + + +```math +\begin{align*} +Y[a, b, \ldots, z] = X[&\text{S'}[t_0] + a \cdot \text{K}[t_0], \\ + &\text{S'}[t_1] + b \cdot \text{K}[t_1], \\ + &\vdots \\ + &\text{S'}[t_{r-1}] + z \cdot \text{K}[t_{r-1}]] +\end{align*} +``` + +Where: +- $r$ is the rank of tensor `X` +- $a \in [0, dY_0-1]$ is the index along the first dimension of output tensor `Y` +- $b \in [0, dY_1-1]$ is the index along the second dimension of output tensor `Y` +- $z \in [0, dY_{r-1}-1]$ is the index along the last dimension of output tensor `Y` +- $i \in \{0, ...\ r-1\}$ is the index of the current axis +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ +- $S'$ is the clamped tensor `S`. For each entry $S[i]$: + - If $S[i] < 0$, then $S'[i] = S[i] + dX_{A'[i]}$ + + - Otherwise, $S'[i] = S[i]$ +- $E'$ is the clamped tensor `E`. For each entry $E[i]$: + - If $E[i] < 0$, then $E'[i] = E[i] + dX_{A'[i]}$ + + - Otherwise, $E'[i] = E[i]$ + +- $space_{i} = E'[i] - S'[i]$ is the available space along axis `i` for slicing +```math + f = \begin{cases} 0 & \text{if}\quad (space_{i} \bmod K[i] = 0) \\ 1 & \text{otherwise} \end{cases} +\quad \text{is a flag indicating whether there is a remainder when dividing the available space by the step size along axis i} +``` +- $t_z \in [0, r-1]$ is the index in which axis `z` is represented in tensor $A'$ +- $dY_{A'[i]} = \left\lfloor \frac{\text{space}_{i}}{K[i]} \right\rfloor + f$ is the dimension of the output tensor `Y` along axis `i` + +\ +\ +The effect of the operator is illustrated on the following figure. In this example +- shape of `X` is ($5, 6$) +- shape of `A` is ($2$) with values `[0, 1]` +- shape of `S` is ($2$) with values `[0, 1]` +- shape of `E` is ($2$) with values `[4, 6]` +- shape of `K` is ($2$) with values `[1, 2]` + +Once there are no negative values for `S` and `E`, the clamped tensors `S'` and `E'` are equal to `S` and `E`, respectively. + + +Finally, the following figure illustrates operator Slice applied on input `X` with the above parameters, resulting in output `Y` with shape ($4, 3$). + +drawing + +In order to understand how the shape of output `Y` is computed, the following figure illustrates the slicing process along each axis. + +For the horizontal axis and starting from the black square we can do two steps and the third one is incomplete. Once there is an incomplete step we have to add one ( [f](#f) - ensuring that the dimension is equal to the total number of steps, completed or not). In this case it is clear that the dimension should be three yet only 2 steps were completed. + +For the vertical axes there is no incomplete steps and the dimension is the same as the number of steps completed. +drawing + +## Error conditions +No error condition + +## Inputs + +### $X$: `real tensor` +Tensor `X` is the input tensor from which a subtensor will be extracted. + +### Constraints + +- `[C1]` Dimensional consistency + - Statement: + - $rank(X) = d{S_0} = d{E_0} = d{A_0} = d{K_0}$ + - Rationale: This ensures that the slicing parameters are well-defined for each axis of `X`. [[R2]](#R2) +- `[C2]` Rank consistency + - Statement: The rank of tensor `X` and `Y` must be the same. + - Rationale: Slicing does not change the rank of the tensor. +- `[C3]` Minimum rank + - Statement: The rank of tensor `X` must be at least 1. + - Rationale: Slicing is not defined for scalars. + +### $S$: `integer tensor` +Tensor `S` is a tensor containing the starting indices for each axis specified in `A`. + +Tensor `S` must be a 1-D tensor. + +### Constraints +- `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + +- `[C2]` Value Domain + - Statement: The adjusted starting indices must be clamped to valid ranges. +```math +\forall i \in [0, r-1], \quad S[i] \in [-d{X_{A'_{[i]}}}, d{X_{A'_{[i]}}}-1] +``` + +Where: +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + - Otherwise, $A'[i] = A[i]$ +- Rationale: Starting indices must be within the valid range of indices for tensor `X`, adjusted for negative indexing. Accouting for start inclusivity the maximum valid index is $dX_i - 1$. [[R8]](#R8) + +- `[C3]` Steps and starting/ending indices consistency + - Statement: Ensuring that output dimensions are positive and follow this [formula](#dY). [[R6]](#R6) [[R7]](#R7) + + + +### $E$: `integer tensor` +Tensor `E` is a tensor containing the ending indices (exclusive) for each axis specified in `A`. + +Tensor `E` must be a 1-D tensor. + +### Constraints +- `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + +- `[C2]` Value Domain + - Statement: The adjusted ending indices must be clamped to valid ranges based on the stepping direction. +```math +\forall i \in [0, r-1], \quad +E[i] \in +\begin{cases} +[-d{X_{A'_{[i]}}}, d{X_{A'_{[i]}}}] & \text{if } K[i] > 0 \\ +[-d{X_{A'_{[i]}}}-1, d{X_{A'_{[i]}}} -1] & \text{if } K[i] < 0 +\end{cases} +``` +Where +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ + +- `[C3]` Steps and starting/ending indices consistency + - Statement: see constraint [[C3]](#C8ra) on tensor `S`. [[R6]](#R6) [[R7]](#R7) + + +### $A$: `integer tensor` +Tensor `A` is a tensor containing the axes along which to slice. + +Tensor `A` must be a 1-D tensor. +### Constraints + - `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + + - `[C2]` Value Domain + - Statement: Each axis specified in `A` must be a valid axis index for tensor `X`. +```math + \forall i \in [0, r-1], \quad + A[i] \in [-r, r-1] +``` +Where +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- Rationale: This ensures that the slicing axes are valid for the input tensor. + - `[C3]` Uniqueness + - Statement: After normalizing negative indices, all axes in `A` must be unique: + $$\forall i, j \in [0, r-1], \; (A[i] + r) \bmod r = ((A[j] + r) \bmod r)\implies (i = j)$$ + - Rationale: Prevents ambiguity when the same axis is specified multiple times using different representations (negative and positive). + +### $K$: `integer tensor` +Tensor `K` is a tensor containing the steps for each axis specified in `A`. + +Tensor `K` must be a 1-D tensor. + +### Constraints + - `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ra) on tensor `X`. + + - `[C2]` Value Domain + - Statement: Each step in `K` must be a valid step size for the corresponding axis in `A`. +```math + \forall i \in [0, r-1], \quad + K[i] \in \mathbb{Z} \setminus \{0\} + ``` +Where + - $r$ is the rank of tensor `X`. + - $i$ is the axis index. +- Rationale: This ensures that the steps are well-defined for each axis of `X`. + + - `[C3]` Steps and starting/ending indices consistency + - Statement: see constraint [[C3]](#C8ra) on tensor `S`. [[R6]](#R6) [[R7]](#R7) + + +## Outputs + +### $Y$: `real tensor` +Tensor `Y` is the output tensor containing the sliced subtensor from `X`. +### Constraints + - `[C1]` Rank consistency + - Statement: see constraint [[C2]](#C2ra) on tensor `X`. + + - `[C2]` Consistency between the shape of tensors `S`, `E`, `A`, `K`, and `Y` + - Statement: + - $\forall i \in [0, r-1], \quad dY_{A'[i]} = \left\lfloor \frac{\text{space}_{i}}{K[i]} \right\rfloor + f \gt 0$ + + - Rationale: The size of the output tensor `Y` is determined by the slicing parameters applied to tensor `X`. + + Where + - $A'$ is the clamped tensor `A`. For each entry A[i]: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ + - $S'$ is the clamped tensor `S`. For each entry S[i]: + - If $S[i] < 0$, then $S'[i] = S[i] + dX_{A'[i]}$ + + - Otherwise, $S'[i] = S[i]$ + - $E'$ is the clamped tensor `E`. For each entry E[i]: + - If $E[i] < 0$, then $E'[i] = E[i] + dX_{A'[i]}$ + + - Otherwise, $E'[i] = E[i]$ + + - $space_{i} = E'[i] - S'[i]$ is the available space along axis `i` for slicing +```math + f = \begin{cases} 0 & \text{if}\quad (space_{i} \bmod K[i] = 0) \\ 1 & \text{otherwise} \end{cases} +\quad \text{is a flag indicating whether there is a remainder when dividing the available space by the step size along axis i} +``` + - `[C3]` Value Consistency + - Statement: Each element in tensor `Y` must correspond to the appropriate sliced element from tensor `X` based on the slicing parameters. [Y](#Y) + + - Rationale: Ensures that the output tensor `Y` accurately reflects the slicing operation performed on tensor `X`. + + +## Attributes + +Operator **slice** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **slice** does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. + +# **slice** (type, itype, itype, itype, itype) + +Where type is in {INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, DOUBLE, BFLOAT16, BOOL, STRING} + +Where itype is in {INT32, INT64} + +## Signature +$Y = \text{slice}(X,S,E,A,K)$ + +where: +- `X`: input tensor +- `A`: 1-D tensor of axes to slice +- `S`: 1-D tensor with starting indices of corresponding axis in `A` +- `E`: 1-D tensor with ending indices of corresponding axis in `A` +- `K`: 1-D tensor of steps for each axis in `A` +- `Y`: output tensor + + +## Restrictions +The following restrictions apply to the **slice** operator for the SONNX profile: + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | Input `A` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R2]` | All axes must be specified for input `A` | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R3]` | Input `K` must be set | [No default values](../../../deliverables/reqs/reqs.md#no_default_value) | +| `[R4]` | Sparse tensors are not supported | General restriction [GR1](../general_restrictions.md#GR1) | +| `[R5]` | Shape of tensors shall be explicit | General restriction [GR2](../general_restrictions.md#GR2) | +| `[R6]` | Positive steps must have starting positions lower or equal than ending positions | Transient | +| `[R7]` | Negative steps must have starting positions greater or equal than ending positions | Transient | +| `[R8]` | For all axis of the input tensor, the start value, on tensor `S`, that represent that axis must be lower than the axis dimension | Transient | +| `[R9]` | `X` and `Y` tensor must have the same type | General restriction [GR3](../general_restrictions.md#GR3) | +| `[R10]` | `A`, `S`, `E` and `K` must have the same type | General restriction [GR3](../general_restrictions.md#GR3) | + +## Informal specification + + +Operator **slice** extracts from `X` a subtensor defined by the axes listed in `A`. + +For each axis, `i` of the input tensor, slicing: +- starts at $S'[i]$ +- slides with a step of $K[i]$ +- stops strictly before $E'[i]$ + +The result is stored in output tensor `Y`. + + +```math + \begin{align*} +Y[a, b, \ldots, z] = X[&\text{S'}[t_0] + a \cdot \text{K}[t_0], \\ + &\text{S'}[t_1] + b \cdot \text{K}[t_1], \\ + &\vdots \\ + &\text{S'}[t_{r-1}] + z \cdot \text{K}[t_{r-1}]] +\end{align*} +``` + +Where: +- $r$ is the rank of tensor `X` +- $a \in [0, dY_0-1]$ is the index along the first dimension of output tensor `Y` +- $b \in [0, dY_1-1]$ is the index along the second dimension of output tensor `Y` +- $z \in [0, dY_{r-1}-1]$ is the index along the last dimension of output tensor `Y` +- $i \in \{0, ...\ r-1\}$ is the index of the current axis~ +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ +- $S'$ is the clamped tensor `S`. For each entry $S[i]$: + - If $S[i] < 0$, then $S'[i] = S[i] + dX_{A'[i]}$ + + - Otherwise, $S'[i] = S[i]$ +- $E'$ is the clamped tensor `E`. For each entry $E[i]$: + - If $E[i] < 0$, then $E'[i] = E[i] + dX_{A'[i]}$ + + - Otherwise, $E'[i] = E[i]$ + +- $space_{i} = E'[i] - S'[i]$ is the available space along axis `i` for slicing +```math + f = \begin{cases} 0 & \text{if}\quad (space_{i} \bmod K[i] = 0) \\ 1 & \text{otherwise} \end{cases} +\quad \text{is a flag indicating whether there is a remainder when dividing the available space by the step size along axis i} +``` +- $t_z \in [0, r-1]$ is the index in which axis `z` is represented in tensor $A'$ +- $dY_{A'[i]} = \left\lfloor \frac{\text{space}_{i}}{K[i]} \right\rfloor + f$ is the dimension of the output tensor `Y` along axis `i` + +\ +\ +The effect of the operator is illustrated on the following figure. In this example +- shape of `X` is ($5, 6$) +- shape of `A` is ($2$) with values `[0, 1]` +- shape of `S` is ($2$) with values `[0, 1]` +- shape of `E` is ($2$) with values `[4, 6]` +- shape of `K` is ($2$) with values `[1, 2]` + +Once there are no negative values for `S` and `E`, the clamped tensors `S'` and `E'` are equal to `S` and `E`, respectively. + + +Finally, the following figure illustrates operator Slice applied on input `X` with the above parameters, resulting in output `Y` with shape ($4, 3$). + +drawing + +In order to understand how the shape of output `Y` is computed, the following figure illustrates the slicing process along each axis. + +For the horizontal axis and starting from the black square we can do two steps and the third one is incomplete. Once there is an incomplete step we have to add one ( [f](#f) - ensuring that the dimension is equal to the total number of steps, completed or not). In this case it is clear that the dimension should be three yet only 2 steps were completed. + +For the vertical axes there is no incomplete steps and the dimension is the same as the number of steps completed. +drawing + +## Error conditions +No error condition + +## Inputs + +### $X$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, DOUBLE, BFLOAT16, BOOL, STRING +Tensor `X` is the input tensor from which a subtensor will be extracted. + +### Constraints + +- `[C1]` Dimensional consistency + - Statement: + - $rank(X) = d{S_0} = d{E_0} = d{A_0} = d{K_0}$ + - Rationale: This ensures that the slicing parameters are well-defined for each axis of `X`. [[R2]](#tR2) +- `[C2]` Rank consistency + - Statement: The rank of tensor `X` and `Y` must be the same. + - Rationale: Slicing does not change the rank of the tensor. +- `[C3]` Minimum rank + - Statement: The rank of tensor `X` must be at least 1. + - Rationale: Slicing is not defined for scalars. +- `[C4]` Type consistency + - Statement: Tensors `X` and `Y` must have the same type. [[R9]](#tR9) + +### $S$: INT32, INT64 +Tensor `S` is a tensor containing the starting indices for each axis specified in `A`. + +Tensor `S` must be a 1-D tensor. + +### Constraints +- `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ta) on tensor `X`. + +- `[C2]` Value Domain + - Statement: The adjusted starting indices must be clamped to valid ranges. +```math +\forall i \in [0, r-1], \quad S[i] \in [-d{X_{A'_{[i]}}}, d{X_{A'_{[i]}}}-1] +``` +Where +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ +- Rationale: Starting indices must be within the valid range of indices for tensor `X`, adjusted for negative indexing. Accouting for start inclusivity the maximum valid index is $dX_i - 1$. [[R8]](#tR8) + +- `[C3]` Steps and starting/ending indices consistency + - Statement: Ensuring that output dimensions are non negative and follow this [formula](#tdY). [[R6]](#tR6) [[R7]](#tR7) +- `[C4]` Type consistency + - Statement: Tensors `A`, `S`, `E`, and `K` must have the same type. [[R10]](#tR10) + + + +### $E$: INT32, INT64 +Tensor `E` is a tensor containing the ending indices (exclusive) for each axis specified in `A`. + +Tensor `E` must be a 1-D tensor. + +### Constraints +- `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ta) on tensor `X`. + +- `[C2]` Value Domain + - Statement: The adjusted ending indices must be clamped to valid ranges based on the stepping direction. + ```math + \forall i \in [0, r-1], \quad + E[i] \in + \begin{cases} + [-d{X_{A'_{[i]}}}, d{X_{A'_{[i]}}}] & \text{if } K[i] > 0 \\ + [-d{X_{A'_{[i]}}}-1, d{X_{A'_{[i]}}} -1] & \text{if } K[i] < 0 + \end{cases} + ``` +Where +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ + +- `[C3]` Steps and starting/ending indices consistency + - Statement: see constraint [[C3]](#C8ta) on tensor `S`. [[R6]](#tR6) [[R7]](#tR7) +- `[C4]` Type consistency + - Statement: see constraint [[C4]](#C21ta) on tensor `S`. + + +### $A$: INT32, INT64 +Tensor `A` is a tensor containing the axes along which to slice. + +Tensor `A` must be a 1-D tensor. +### Constraints + - `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ta) on tensor `X`. + + - `[C2]` Value Domain + - Statement: Each axis specified in `A` must be a valid axis index for tensor `X`. +```math +\forall i \in [0, r-1], \quad +A[i] \in [-r, r-1] +``` +Where +- $r$ is the rank of tensor `X`. +- $i$ is the axis index. +- Rationale: This ensures that the slicing axes are valid for the input tensor. + - `[C3]` Uniqueness + - Statement: After normalizing negative indices, all axes in `A` must be unique: + $$\forall i, j \in [0, r-1], \; (A[i] + r) \bmod r = ((A[j] + r) \bmod r)\implies (i = j)$$ + - Rationale: Prevents ambiguity when the same axis is specified multiple times using different representations (negative and positive). + - `[C4]` Type consistency + - Statement: see constraint [[C4]](#C21ta) on tensor `S`. + +### $K$: INT32, INT64 +Tensor `K` is a tensor containing the steps for each axis specified in `A`. + +Tensor `K` must be a 1-D tensor. + +### Constraints + - `[C1]` Dimensional consistency + - Statement: see constraint [[C1]](#C1ta) on tensor `X`. + + - `[C2]` Value Domain + - Statement: Each step in `K` must be a valid step size for the corresponding axis in `A`. +```math + \forall i \in [0, r-1], \quad + K[i] \in \mathbb{Z} \setminus \{0\} + ``` + Where + - $r$ is the rank of tensor `X`. + - $i$ is the axis index. +- Rationale: This ensures that the steps are well-defined for each axis of `X`. + + - `[C3]` Steps and starting/ending indices consistency + - Statement: see constraint [[C3]](#C8ta) on tensor `S`. [[R6]](#tR6) [[R7]](#tR7) + - `[C4]` Type consistency + - Statement: see constraint [[C4]](#C21ta) on tensor `S`. + + +## Outputs + +### $Y$: INT8, INT16, INT32, INT64, UINT8, UINT16, UINT32, UINT64, FP16, FP32, DOUBLE, BFLOAT16, BOOL, STRING +Tensor `Y` is the output tensor containing the sliced subtensor from `X`. +### Constraints + - `[C1]` Rank consistency + - Statement: see constraint [[C2]](#C2ta) on tensor `X`. + + - `[C2]` Consistency between the shape of tensors `S`, `E`, `A`, `K`, and `Y` + - Statement: + - $\forall i \in [0, r-1], \quad dY_{A'[i]} = \left\lfloor \frac{\text{space}_{i}}{K[i]} \right\rfloor + f \gt 0$ + + - Rationale: The size of the output tensor `Y` is determined by the slicing parameters applied to tensor `X`. + + Where + - $A'$ is the clamped tensor `A`. For each entry $A[i]$: + - If $A[i] < 0$, then $A'[i] = A[i] + r$ + + - Otherwise, $A'[i] = A[i]$ + - $S'$ is the clamped tensor `S`. For each entry S[i]: + - If $S[i] < 0$, then $S'[i] = S[i] + dX_{A'[i]}$ + + - Otherwise, $S'[i] = S[i]$ + - $E'$ is the clamped tensor `E`. For each entry E[i]: + - If $E[i] < 0$, then $E'[i] = E[i] + dX_{A'[i]}$ + + - Otherwise, $E'[i] = E[i]$ + + - $space_{i} = E'[i] - S'[i]$ is the available space along axis `i` for slicing +```math + f = \begin{cases} 0 & \text{if}\quad (space_{i} \bmod K[i] = 0) \\ 1 & \text{otherwise} \end{cases} +\quad \text{is a flag indicating whether there is a remainder when dividing the available space by the step size along axis i} +``` + - `[C3]` Value Consistency + - Statement: Each element in tensor `Y` must correspond to the appropriate sliced element from tensor `X` based on the slicing parameters. [Y](#tY) + + - Rationale: Ensures that the output tensor `Y` accurately reflects the slicing operation performed on tensor `X`. + - `[C4]` Type consistency + - Statement: see constraint [[C4]](#C20ta) on tensor `X`. + + +## Attributes + +Operator **slice** has no attribute. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +Operator **slice** does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. diff --git a/safety-related-profile/sonnx/ops/spec/informal/softmax/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/softmax/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/softmax/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/softmax/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/softmax/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/softmax/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/softmax/softmax.md b/safety-related-profile/sonnx/ops/spec/informal/softmax/softmax.md new file mode 100644 index 00000000..d008cca2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/softmax/softmax.md @@ -0,0 +1,427 @@ +# Contents + +- **Softmax** operator for type [real](#real) +- **Softmax** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Softmax version 13](https://onnx.ai/onnx/operators/onnx__Softmax.html). + + +# **Softmax** (real) + +## Signature + +$Y = \textbf{Softmax}(X)$ + +where: + +- $X$: input tensor +- $Y$: output tensor + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +| Restriction | Statement | Origin | +|-------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------| +| `[R1]` | `axis` $\ge 0$ | Transient | +--- + +## Informal specification +[t1] + +The **Softmax** operator computes the normalized exponential function along a specified axis of the input tensor. + +Let $r$ be the rank of $X$ and let $\textit{axis}$ be the attribute specifying the dimension along which the normalization is performed. + +For any [tensor index](./../common/definitions.md#tensor_index) +$i = (i_0, \dots, i_{r-1})$: + + +> Rajouter la formulation avec le "-M". + + +$$ +Y[i] = +\frac{e^{X[i]}} +{\sum_{j_{axis}=0}^{dX_{axis}-1} +e^{X[i_0,\dots,j_{axis},\dots,i_{r-1}]}} +$$ + +where: + +* $dX_{axis}$ denotes the size of tensor $X$ along dimension `axis`, +* the summation is performed over the `axis` dimension only, +* all other indices remain fixed. + +The operator preserves the shape of the input tensor. + + + +### Example 1 + +Let: + +```math +X = \begin{bmatrix} 9.5 & 35.7 \end{bmatrix} +``` + +with `axis = 0`. + +Then: + +```math +Y = \textbf{Softmax}(X) += +\begin{bmatrix} +\frac{e^{9.5}}{e^{9.5}+e^{35.7}} & +\frac{e^{35.7}}{e^{9.5}+e^{35.7}} +\end{bmatrix} +``` + + + +## Error conditions + +No error condition. + +Since exponentials are strictly positive, the denominator is strictly positive and division by zero cannot occur. + +## Attributes + +### `axis`: int + +Specifies the dimension along which the **Softmax** operation is performed. + +#### Constraints + +- `[C1]` Value domain + + - Statement: `axis` ≥ 0. `[R4]` + +- `[C2]` Consistency with tensor rank + + - Statement: `axis` < r, where $r$ is the rank of $X$. + + + +## Inputs + +### $\text{X}$: real + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: $X$ and $Y$ shall have the same shape. + +- `[C2]` Consistency with `axis` + + - Statement: The rank $r$ of $X$ shall satisfy `axis` < r. + +## Outputs + +### $\text{Y}$: real + +Softmax of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: See constraint [[C1]](#C1ra) on tensor $X$. + + + +# **Softmax** (float) + +where float is in {float16, float, double}. + +## Signature + +Definition of operator $\text{Softmax}$ signature: + +$Y = \textbf{Softmax}(X)$ + +where: + +- $X$: floating-point input tensor +- $Y$: floating-point output tensor + +--- + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +See [Restrictions](#real). + + + +## Informal specification +[t1] + +The **Softmax** operator computes the normalized exponential along the specified axis according to IEEE 754 floating-point semantics. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = \text{Softmax}(X[i]) = +\begin{cases} +\\ +\\ +\\ +\text{NaN}, & +\begin{aligned} +&\text{if } \exists j_{axis} : X[i_0,\dots,j_{axis},\dots,i_{r-1}] \in \text{ \{NaN, +inf\} } +\end{aligned} +\\ +\\ +\\ +0, & +\begin{aligned} +&\text{if } X[i]=-\inf \\ +&\land \text{if } \nexists j_{axis} : X[i_0,\dots,j_{axis},\dots,i_{r-1}] \in \text{ \{NaN, +inf\} } +\end{aligned} +\\ +\\ +\\ +\dfrac{e^{X[i]-M}}{\sum_{j_{axis}=0}^{dX_{axis}-1} e^{X[i_0,\dots,j_{axis},\dots,i_{r-1}]-M}} & +\begin{aligned} +& \text{with}~M = \max_{j_{axis}=0}^{dX_{axis}-1} X[i_0,\dots,j_{axis},\dots,i_{r-1}], \text{ otherwise} +\end{aligned} +\end{cases} +$$ + +[/t1] + + +> Normalization using $M$ prevents large values for the exponential. + +--- + +### Example 1 + +```math +X = \begin{bmatrix} 9.5 & 35.7 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 4.182965147 \times 10^{-12} & 0.9999999999958 \end{bmatrix} +``` + + +### Example 2a + +```math +X = +\begin{bmatrix} +1 & 2 & 3 \\ +4 & 5 & 6 +\end{bmatrix} +``` + +with `axis = 0`. + +```math +Y \approx +\begin{bmatrix} +\frac{e^{1-4}}{e^{1-4}+e^{4-4}}=0.04742587 & \frac{e^{2-5}}{e^{2-5}+e^{5-5}}=0.04742587 & \frac{e^{3-6}}{e^{3-6}+e^{6-6}}=0.04742587 \\ +\frac{e^{4-4}}{e^{1-4}+e^{4-4}}=0.95257413 & \frac{e^{5-5}}{e^{2-5}+e^{5-5}}=0.95257413 & \frac{e^{6-6}}{e^{3-6}+e^{6-6}}=0.95257413 +\end{bmatrix} +``` +### Example 2b + + +```math +X = +\begin{bmatrix} +1 & 2 & 3 \\ +4 & 5 & 6 +\end{bmatrix} +``` + +with `axis = 1`. + +```math +Y \approx +\begin{bmatrix} +\frac{e^{1-3}}{e^{1-3}+e^{2-3}+e^{3-3}}=0.09003057 & \frac{e^{2-3}}{e^{1-3}+e^{2-3}+e^{3-3}}=0.24472848 & \frac{e^{3-3}}{e^{1-3}+e^{2-3}+e^{3-3}}=0.66524094 \\ +\frac{e^{4-6}}{e^{4-6}+e^{5-6}+e^{6-6}}=0.09003057 & \frac{e^{5-6}}{e^{4-6}+e^{5-6}+e^{6-6}}=0.24472848 & \frac{e^{6-6}}{e^{4-6}+e^{5-6}+e^{6-6}}=0.66524094 +\end{bmatrix} +``` + + +### Example with special value : + +### Case `+inf` + +```math +X = +\begin{bmatrix} +1 & 2 & 3 \\ +4 & 5 & +\inf +\end{bmatrix} +``` + + +```math +Y_{\text{axis=0}} \approx +\begin{bmatrix} +0.04742587 & 0.04742587 & \text{NaN} \\ +0.95257413 & 0.95257413 & \text{NaN} +\end{bmatrix} +``` + +```math +Y_{\text{axis=1}} \approx +\begin{bmatrix} +0.09003057 & 0.24472848 & 0.66524094 \\ +\text{NaN} & \text{NaN} & \text{NaN} +\end{bmatrix} +``` + +### Case `-inf` + +```math +X = +\begin{bmatrix} +1 & 2 & 3 \\ +4 & 5 & -\text{inf} +\end{bmatrix} +``` + + +```math +Y_{\text{axis=0}} \approx +\begin{bmatrix} +0.04742587 & 0.04742587 & 1.0 \\ +0.95257413 & 0.95257413 & 0.0 +\end{bmatrix} +``` + +```math +Y_{\text{axis=1}} \approx +\begin{bmatrix} +0.09003057 & 0.24472848 & 0.66524094 \\ +0.26894143 & 0.73105860 & 0.0 +\end{bmatrix} +``` + +### Case `NaN` + +```math +X = +\begin{bmatrix} +1 & 2 & 3 \\ +4 & 5 & \text{NaN} +\end{bmatrix} +``` + +```math +Y_{\text{axis=0}} \approx +\begin{bmatrix} +0.04742587 & 0.04742587 & \text{NaN} \\ +0.95257413 & 0.95257413 & \text{NaN} +\end{bmatrix} +``` + +```math +Y_{\text{axis=1}} \approx +\begin{bmatrix} +0.09003057 & 0.24472848 & 0.66524094 \\ +\text{NaN} & \text{NaN} & \text{NaN} +\end{bmatrix} +``` + + +--- + +### Example: 3D tensor 2x2x3, softmax along axis=2 (last axis) + +```math +X = +\begin{bmatrix} +\begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \end{bmatrix} \\ +\begin{bmatrix} 10 & 20 & 30 \\ 40 & 50 & 60 \end{bmatrix} +\end{bmatrix} +``` + +- Shape: `(2,2,3)` + +```math +Y \approx +\begin{bmatrix} +\begin{bmatrix} +0.09003 & 0.24473 & 0.66524 \\ +0.09003 & 0.24473 & 0.66524 +\end{bmatrix} \\ +\begin{bmatrix} +2.061 \cdot 10^{-9} & 4.539 \cdot 10^{-5} & 0.99995 \\ +2.061 \cdot 10^{-9} & 4.539 \cdot 10^{-5} & 0.99995 +\end{bmatrix} +\end{bmatrix} +``` + + + + + +## Error conditions + +No error condition. + +Division by zero cannot occur because the denominator is strictly positive unless all exponentials underflow to zero, in which case IEEE 754 rules apply. + + + +## Attributes + +### `axis`: int + +See [Softmax (real)](#real). + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: $X$ and $Y$ shall have the same shape. + +- `[C2]` Type consistency + + - Statement: $X$ and $Y$ shall have the same floating-point type. + +- `[C3]` Axis validity + + - Statement: `axis < r`, where $r$ is the rank of $X$. + + + +## Outputs + +### $\text{Y}$: floating-point tensor + +Softmax of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: See constraint [[C1]](#C1fx). + +- `[C2]` Type consistency + + - Statement: See constraint [[C2]](#C2fx). + + + +## Numeric accuracy + +[See the numeric accuracy note](./softmax_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/sqrt/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/sqrt/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sqrt/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/sqrt/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sqrt/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/sqrt/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt.md b/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt.md new file mode 100644 index 00000000..c8726376 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt.md @@ -0,0 +1,218 @@ +# Contents + +- **Sqrt** operator for type [real](#real) +- **Sqrt** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Sqrt version 13](https://onnx.ai/onnx/operators/onnx__Sqrt.html). + + +# **Sqrt** (real) + +## Signature +$Y = \textbf{Sqrt}(X)$ + +where: +- $X$: Input tensor +- $Y$: Square root of $X$ + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sqrt** operator, besides domain constraints explicitly stated below. + +## Informal specification + +The **Sqrt** operator computes the element-wise square root of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +**Sqrt** is only defined for positive values. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = \sqrt{X[i]} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 1 & 2 & 4 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 1 & 1.41421354 & 2 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + 0.25 & 2.25 \\ + 0.0 & 0.1 \\ + 10 & 1000 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.5 & 1.5 \\ + 0.0 & 0.31622776 \\ + 3.16227770 & 31.62277603 +\end{bmatrix} +``` + +## Error conditions + +No error condition beyond the undefined behavior for negative inputs in the mathematical model. + +## Attributes + +Operator **Sqrt** has no attribute. + +## Inputs + +### $\text{X}$: real + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Definition domain + - Statement: $\forall i,\ X[i] \ge 0$. + +## Outputs + +### $\text{Y}$: real + +Square root of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1rx). + + +# **Sqrt** (float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Sqrt}$ signature: +$Y = \textbf{Sqrt}(X)$ + +where: +- $X$: Input tensor +- $Y$: Square root of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sqrt** operator. + +## Informal specification + +The **Sqrt** operator computes the element-wise square root of the input tensor $X$ according to IEEE 754 floating-point semantics. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = +\begin{cases} +\text{NaN} & \text{if } X[i]=\text{NaN} \\ +\text{NaN} & \text{if } X[i] \in [\text{-inf}, \text{-0}[ \\ +\text{-0} & \text{if } X[i]=\text{-0} \\ +\text{inf} & \text{if } X[i] = \text{inf} \\ +\sqrt{X[i]} & \text{otherwise} \\ +\end{cases} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 +```math +X = \begin{bmatrix} 1.0 & 2.0 & 4.0 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 1.0 & 1.41421354 & 2.0 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + 0.25 & -1.0 \\ + 0.0 & 0.1 \\ + 10.0 & -1000.0 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 0.5 & \text{NaN} \\ + 0.0 & 0.31622776 \\ + 3.16227770 & \text{NaN} +\end{bmatrix} +``` + +### Example 3 + +```math +X = \begin{bmatrix} + \text{+inf} & \text{NaN} & \text{-inf} & -0.0 +\end{bmatrix} +``` + +```math +Y = \begin{bmatrix} + \text{+inf} & \text{NaN} & \text{NaN} & -0.0 +\end{bmatrix} +``` + +## Error conditions + +The function returns $\text{NaN}$ when the input is strictly negative and different from -0. + +## Attributes + +Operator **Sqrt** has no attribute. + +## Inputs + +### $\text{X}$: floating-point tensor + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: $X$ and $Y$ shall have the same floating-point type. + +## Outputs + +### $\text{Y}$: floating-point tensor + +Square root of tensor $X$ (with IEEE 754 handling of zero, negative, and infinite inputs). + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1fx). +- `[C2]` Type consistency + - Statement: See [constraint (C2) on X](#C2fx). + +## Numeric accuracy + +[See the numeric accuracy note](./sqrt_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt_acc.md b/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt_acc.md new file mode 100644 index 00000000..0286ef9e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sqrt/sqrt_acc.md @@ -0,0 +1,54 @@ +## Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +### Error Propagation + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Sqrt** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = \sqrt{X}$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\sqrt{x}$ is $d\sqrt{x}/dx = 1/(2\sqrt{x})$, a first-order bound is: + +- For every index $I$ such that $X[I] > 0$ and $X[I] + X_{\textit{err}}[I] \ge 0$ (no crossing of the singularity at 0): + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le \left|\frac{X_{\textit{err}}[I]}{2\sqrt{X[I]}}\right|$ + +- If $X[I]$ and $X[I] + X_{\textit{err}}[I]$ have different signs or approach zero too closely, the bound may become very large (square root near its singularity at 0). + +### Error Introduction +Error introduction for real (ideal) arithmetic is null: +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +### Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto result = [&X](auto I) { + // Real-domain sqrt, undefined for negative inputs + return (X[I].real >= 0) ? sqrt(X[I]) + : SymbolicDomainError::undef(); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + + // Ensure we stay in the domain of sqrt under perturbation + if (x.real > 0 && x.real + x.err >= 0) { + auto y = result(I); + + // First-order propagated error bound: |err_sqrt| <= |err_x / (2*sqrt(x))| + double bound = std::abs(x.err / (2.0 * std::sqrt(x.real))); + + assert(std::abs(y.err) <= bound + 1e-12); + } +} +``` diff --git a/safety-related-profile/sonnx/ops/spec/informal/sub/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/sub/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sub/assets/imgs/README.md b/safety-related-profile/sonnx/ops/spec/informal/sub/assets/imgs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sub/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/sub/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/sub/reviews/jean.md b/safety-related-profile/sonnx/ops/spec/informal/sub/reviews/jean.md new file mode 100644 index 00000000..8b080c32 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sub/reviews/jean.md @@ -0,0 +1,17 @@ +Rmk1: Typos. Taken into account in sub.md + +Rmk2: In ONNX, the name of the operator is **S**ub, not sub. Taken into account in sub.md + +Rmk3: Contents section. + +* ONNX bfloat16 type is in the ONNX description of Sub but not in SONNX sub.md. If this type is never used in SONNX, it has to be inserted as a general restriction. + +* INT4 and UINT4 are in SONNX sub.md but not in the ONNX description of Sub. Taken into account in sub.md. + +* Add reference to the ONNX documentation. Taken into account in sub.ld + +Rmk4: broken links. Already fixed. + +Rmk5: Replace the type names by their counterpart in ONNX. Example: replace "FP64" by "double". Taken into account in sub.md. + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/sub/sub.md b/safety-related-profile/sonnx/ops/spec/informal/sub/sub.md new file mode 100644 index 00000000..eb0d3cd0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/sub/sub.md @@ -0,0 +1,329 @@ +# Contents + - **Sub** operator for type [real](#real) + - **Sub** operator for types [float16, float, double](#float) + - **Sub** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation version 14. + +--- + + +# **Sub** (real, real) + +## Signature + +Definition of operator $\text{Sub}$ signature: + +$C = \text{Sub}(A, B)$ + +where: +- $A$: first operand of the subtraction +- $B$: second operand of the subtraction +- $C$: result of the element-wise subtraction of $B$ from $A$ + + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sub** operator. + +## Function + + +[E_SUB_REAL_FUNC_0010]
+ +Operator **Sub** subtracts input tensors $B$ from input tensor $A$ element-wise and stores the result in output tensor $C$. Each element $C[i]$ is the result of subtracting $B[i]$ from $A[i]$ where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The definition of the operator is given hereafter. + +For any index i: + +$$ +C[i] = A[i] - B[i] +$$ + +[END]
+ +The effect of the operator is illustrated on the following examples: + +### Example 1 (1D tensors) + +```math +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +``` + +```math +B = \begin{bmatrix} 2 & 3 & 4 \end{bmatrix} +``` + +```math +C = A - B = \begin{bmatrix} 4.1 & 6.5 & 31.7 \end{bmatrix} +``` + +--- + +## Error conditions +No error condition. + +## Attributes + +The **Sub** operator has no attribute. + +## Inputs + +### $\text{A}$: real tensor +Tensor $A$ is the first operand of the subtraction. + +#### Constraints + + + - `[E_SUB_REAL_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + + +### $\text{B}$: real tensor +Tensor $B$ is the second operand of the subtraction. + +#### Constraints + + - `[E_SUB_REAL_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_SUB_REAL_CONSTR_A_0010](#E_SUB_REAL_CONSTR_A_0010) on tensor $A$. + +## Outputs + +### $\text{C}$: real tensor + +Tensor $C$ is the element-wise result of $A$ Subtiplied by $B$. + +#### Constraints + + - `[E_SUB_REAL_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_SUB_REAL_CONSTR_A_0010](#E_SUB_REAL_CONSTR_A_0010) on tensor $A$. + + +--- + + +# **Sub** (float, float) +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Sub}$ signature: + +$C = \text{Sub}(A, B)$ + +where + + - $A$: first operand tensor + - $B$: second operand tensor + - $C$: output tensor, result of element-wise subtraction of $B$ from $A$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sub** operator. + +## Function + + +[E_SUB_FLOAT_FUNC_0010]
+ +Operator **Sub** subtracts input tensor $B$ from input ensor $A$ element-wise according to IEEE 754 floating-point semantics, placing the result in output tensor $C$. Each element $C[i]$ is computed as follows: + +$$ +C[i] = A[i] - B[i] +$$ + +[END]
+ +--- + +### Example 1 (2D tensors) + +```math +A = \begin{bmatrix} 3.0 & 4.5 \\ 16.0 & 1.0 \\ 25.5 & 24.25 \end{bmatrix} +\quad +B = \begin{bmatrix} 3.0 & 2.0 \\ 4.0 & 0.0 \\ 5.0 & 4.0 \end{bmatrix} +``` + +```math +C = A - B = \begin{bmatrix} 0.0 & 2.5 \\ 12.0 & 1.0 \\ 20.5 & 20.25 \end{bmatrix} +``` +## Error conditions +No error condition. + +## Attributes + +The **Sub** operator has no attribute. + +## Inputs + +### $\text{A}$: floating-point tensor +Tensor $A$ is the first operand of the subtraction. + +#### Constraints + + + - `[E_SUB_FLOAT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +- `[E_SUB_FLOAT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + + +### $\text{B}$: floating-point tensor +Tensor $B$ is the second operand of the subtraction. + +#### Constraints + + - `[E_SUB_FLOAT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_SUB_FLOAT_CONSTR_A_0010](#E_SUB_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_SUB_FLOAT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_SUB_FLOAT_CONSTR_A_0020](#E_SUB_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: floating-point tensor + +Tensor $C$ is the element-wise result of $A$ Subtiplied by $B$. + +#### Constraints + + - `[E_SUB_FLOAT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_SUB_FLOAT_CONSTR_A_0010](#E_SUB_FLOAT_CONSTR_A_0010) on tensor $A$. +- `[E_SUB_FLOAT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_SUB_FLOAT_CONSTR_A_0020](#E_SUB_FLOAT_CONSTR_A_0020) on tensor $A$. + + +--- + + + +# **Sub** (int, int) +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature +Definition of operator $\text{Sub}$ signature: + + $C = \text{Sub}(A,B)$ + + where + - $A$: first operand of the subtraction + - $B$: second operand of the subtraction + - $C$: result of the element-wise subtraction of $B$ from $A$ + +## Restrictions +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Sub** operator. + +## Function + + +[E_SUB_INT_FUNC_0010]
+ +Operator **Sub** subtracts input tensor $B$ from input tensor $A$ element-wise and stores the result in output tensor $C$. Each element $C[i]$ is the result of subtracting $B[i]$ from $A[i]$ where $i$ is a [tensor index](../common/definitions.md#tensor_index). + +The integer subtraction is performed as follows (considering that all tensors have the same type): + +For unsigned values (type uint\): + +$$ +C[i]= +\begin{cases} + A[i] - B[i] + 2^{n} & \quad \textrm{if } A[i] < B[i] \\ + A[i] - B[i] & \quad \textrm{otherwise} +\end{cases} +$$ + +For signed values (type int\): + +$$C[i]= +\begin{cases} + A[i] - B[i] - 2^{n} & \quad \textrm{if } A[i] - B[i] > 2^{n-1}-1 \\ + A[i] - B[i] + 2^{n} & \quad \textrm{if } A[i] - B[i] < -2^{n-1} \\ + A[i] - B[i] & \quad \textrm{otherwise} +\end{cases} +$$ + +[END]
+ +### Example 1 (1D uint8 tensors) + +```math +A = \begin{bmatrix} 6 & 100 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 200 \end{bmatrix} +``` +```math +C = \begin{bmatrix} 3 & 44 \end{bmatrix} +``` + +### Example 2 (1D int8 tensors) + +```math +A = \begin{bmatrix} -6 & 10 & 10 \end{bmatrix} +\quad +B = \begin{bmatrix} -3 & 100 & -120 \end{bmatrix} +``` +```math +C = \begin{bmatrix} -9 & -90 & -126 \end{bmatrix} +``` + +## Error conditions +- According to the definition, the result of the subtraction differs from the value that would be expected in $N$ (for unsigned) or $Z$ (for signed) when under- or overflow occur. + +## Attributes + +The **Sub** operator has no attribute. + +## Inputs + +### $\text{A}$: integer tensor + +Tensor $A$ is the first operand of the subtraction. + +#### Constraints +This section gives all constraints applicable to the input. + + +- `[E_SUB_INT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$ and $C$ must have the same shape. + +- `[E_SUB_INT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + + +### $\text{B}$: integer tensor + +Tensor $B$ is the second operand of the subtraction. + +#### Constraints + +- `[E_SUB_INT_CONSTR_B_0010]` Shape consistency + - Statement: see constraint [E_SUB_INT_CONSTR_A_0010](#E_SUB_INT_CONSTR_A_0010) on tensor $A$. +- `[E_SUB_INT_CONSTR_B_0020]` Type consistency + - Statement: see constraint [E_SUB_INT_CONSTR_A_0020](#E_SUB_INT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: integer tensor + +Tensor $C$ is the element-wise integer subtraction result. + +#### Constraints + + - `[E_SUB_INT_CONSTR_C_0010]` Shape consistency + - Statement: see constraint [E_SUB_INT_CONSTR_A_0010](#E_SUB_INT_CONSTR_A_0010) on tensor $A$. +- `[E_SUB_INT_CONSTR_C_0020]` Type consistency + - Statement: see constraint [E_SUB_INT_CONSTR_A_0020](#E_SUB_INT_CONSTR_A_0020) on tensor $A$. + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/tanh/reviews/README.md b/safety-related-profile/sonnx/ops/spec/informal/tanh/reviews/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh.md b/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh.md new file mode 100644 index 00000000..92ac99a5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh.md @@ -0,0 +1,216 @@ +# Contents + +- **Tanh** operator for type [real](#real) +- **Tanh** operator for types [float16, float, double](#float) + +Based on ONNX documentation [Tanh version 13](https://onnx.ai/onnx/operators/onnx__Tanh.html). + + +# **Tanh** (real) + +## Signature + +$Y = \textbf{Tanh}(X)$ + +where: +- $X$: Input tensor +- $Y$: Hyperbolic tangent of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + + +## Informal specification + +The **Tanh** operator computes the element-wise hyperbolic tangent of the input tensor $X$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Y[i] = \tanh(X[i]) = \frac{e^{X[i]}-e^{-X[i]}}{e^{X[i]}+e^{-X[i]}} = \frac{e^{2X[i]}-1}{e^{2X[i]}+1} = \frac{1 - e^{-2X[i]}}{1 + e^{-2X[i]}} +$$ + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0.0 & 0.76159418 & -0.76159418 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + -0.96402758 & 0.0 \\ + 0.76159418 & 0.96402758 \\ + -0.99932921 & 0.99932921 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Tanh** has no attribute. + +## Inputs + +### $\text{X}$: real + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. + +## Outputs + +### $\text{Y}$: real + +Hyperbolic tangent of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1rx). + + +# **Tanh** (float) +where float is in {float16, float, double}. + +## Signature + +$Y = \textbf{Tanh}(X)$ + +where: +- $X$: Input tensor +- $Y$: Hyperbolic tangent of $X$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + + +## Informal specification + +The **Tanh** operator computes the element-wise hyperbolic tangent of the input tensor $X$ according to IEEE 754 floating-point semantics. + +The definition of the operator is given hereafter. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + + + +$$ +Y[i] = \tanh(X[i]) = +\begin{cases} +\text{-1.0} & \text{if } X[i]=\text{-inf} \\ +\text{1.0} & \text{if } X[i]=\text{inf} \\ +\text{NaN} & \text{if } X[i]=\text{NaN} \\ +\frac{1 - e^{-2X[i]}}{1 + e^{-2X[i]}} & \text{otherwise} \\ +\end{cases} +$$ + + + +The effect of the operator is illustrated on the following examples. + +### Example 1 + +```math +X = \begin{bmatrix} 0 & 1 & -1 \end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} 0.0 & 0.76159418 & -0.76159418 \end{bmatrix} +``` + +### Example 2 + +```math +X = \begin{bmatrix} + -2 & 0 \\ + 1 & 2 \\ + -4 & 4 +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + -0.96402758 & 0.0 \\ + 0.76159418 & 0.96402758 \\ + -0.99932921 & 0.99932921 +\end{bmatrix} +``` + + +### Example 3 + +```math +X = \begin{bmatrix} + \text{+inf} & \text{NaN} & \text{-inf} +\end{bmatrix} +``` + +```math +Y \approx \begin{bmatrix} + 1.0 & \text{NaN} & -1.0 +\end{bmatrix} +``` + +## Error conditions + +No particular error, the function returns $\text{NaN}$ only when the input is $\text{NaN}$ + +## Attributes + +Operator **Tanh** has no attribute. + +## Inputs + +### $\text{X}$: `float` + +Input tensor. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: $X$ and $Y$ shall have the same shape. +- `[C2]` Type consistency + - Statement: $X$ and $Y$ shall have the same floating-point type. + +## Outputs + +### $\text{Y}$: `float` + +Hyperbolic tangent of tensor $X$. + +#### Constraints + +- `[C1]` Shape consistency + - Statement: See [constraint (C1) on X](#C1fx). +- `[C2]` Type consistency + - Statement: See [constraint (C2) on X](#C2fx). + +## Numeric accuracy + +[See the numeric accuracy note](./tanh_acc.md). diff --git a/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh_acc.md b/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh_acc.md new file mode 100644 index 00000000..c4a0a294 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/tanh/tanh_acc.md @@ -0,0 +1,171 @@ +# Numerical Accuracy + +$Y_{\textit{err}} = Y_{\textit{err}}^{\textit{propag}} + Y_{\textit{err}}^{\textit{intro}}$. + +## Note Algorithm +Tanh is subject to exponent overflow when evaluating large positive exponents (e.g. exp(2X) for very positive values of X). +To remain numerically stable in float, the minimal precision algorithm shall split the `X` domain so that only negative exponents are computed. + +``` +if X < 0 + Y = (exp(2X) - 1) / (exp(2X) + 1) +else + Y = (1 - exp(-2X)) / (1 + exp(-2X)) +``` + +## Error Propagation - for information - see [guidelines](../../../docs/guidelines/accuracy.md#error-propagation) + +This section contains properties of $Y_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Y$ is the tensor result of the **Tanh** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). For $Y = \tanh(X)$, the propagated error $Y_{\textit{err}}^{\textit{propag}}$ comes from the input error $X_{\textit{err}}$. + +Using the derivative of $\tanh$ is $d\tanh(x)/dx = 1 - \tanh^2(x)$, a first-order bound is: + +- For every index $I$: + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |1 - \tanh^2(X[I])|\cdot|X_{\textit{err}}[I]| +\mathcal{O}(|X_{\textit{err}}[I]|^2)$ + +- The complete definition of $\mathcal{O}(|X_{\textit{err}}[I]|^2)$ + is available in the [guidelines](../../../docs/guidelines/accuracy.md#error-propagation). +- Since $0 \le 1 - \tanh^2(x) \le 1$ for all real $x$, a global bound is: + - $|Y_{\textit{err}}^{\textit{propag}}[I]| \le |X_{\textit{err}}[I]|$ + +This operator does not amplify the initial error. + +## Error Introduction (real) + +Error introduction for real (ideal) arithmetic is null: + +- $Y_{\textit{err}}^{\textit{intro}} = [0]$. + +## Error Introduction (IEEE-754 floating-point) + +Let us define $\varepsilon$ the [machine epsilon](https://en.wikipedia.org/wiki/Machine_epsilon) +for the considered format and $\textit{\textbf{u}} = \frac{\varepsilon}{2}$. + +The accuracy of the implementation relies of the accuracy of the `exp` available function. +We can consider that this function has a $2\varepsilon$ accuracy in the interval $[-1, 0]$. +With the properties $e^{2x} = (e^x)^2$ and $e^{-x} = \frac{1}{e^x}$, the relative +accuracy of `exp` for any number in $[-2^n, -1]$ can be bound by $(2+n/2)\varepsilon$ if the result +is a normal number. Here is a possible definition of $\textit{err}_{\textit{rel}}(\exp(x))$ + +$$\begin{array}{rcl} + \textit{err}_{\textit{rel}}(\exp(x)) & = & 2\varepsilon \textit{ if } x\in [-1, 0] \\ + \textit{err}_{\textit{rel}}(\exp(x)) & = & (2 + \frac{n}{2})\varepsilon \textit{ if } x\in [-2^n, -1] \textit{ with integer } n >= 0 \textit { and } \exp(x) \textit{ is normal}\\ + \textit{err}_{\textit{rel}}(\exp(x)) & = & 2.5\varepsilon \textit{ if } x\in [0, 1]\\ + \textit{err}_{\textit{rel}}(\exp(x)) & = & (2 + \frac{n+1}{2})\varepsilon \textit{ if } x\in [1, 2^n] \textit{ with integer } n >= 0 \textit { and } \exp(-x) \textit{ is normal}\\ + \end{array}$$ + +Nevertheless, any implementor can claim a better precision for this function. In such a case, +it should adapt the formula below to show that the `tanh` opeator verifies it. + +$$\begin{array}{rcl} + |Y_{\textit{err}}^{\textit{intro}}[I]| & \leq & + \frac{\textit{err}_{\textit{rel}}(\exp(-2|x|))\times 2e^{-2|x|}(1+\textit{\textbf{u}}) + 2\textit{\textbf{u}}(1-e^{-4|x|})}{(1+e^{-2|x|})\left((1+e^{-2|x|})\times(1 - \textit{\textbf{u}}) - \textit{err}_{\textit{rel}}(\exp(-2|x|))\times e^{-2|x|}\times(1+\textit{\textbf{u}})\right)}\times(1 + \textit{\textbf{u}}) + u\times\frac{1-e^{-2|x|}}{1+e^{-2|x|}} + \end{array}$$ + +for the standard rounding mode round to nearest even, provided $e^{-2|x|}$ and $Y_{\textit{val}}[I]$ are +normal numbers. + +For the specification of $\textit{err}_{\textit{rel}}(\exp(x))$ given above, that mean + +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + 8(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - 4\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right)\textit{ if } x\in [-1, 0]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + (8+n)(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - (4+\frac{n}{2})\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{2x}\right) \textit{ if } x\in [-2^n, -1] \textit{ with integer } n >= 0 \textit { and } \exp(x) \textit{ is normal}$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + 8(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - 4\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [0, 1]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + (8+n)(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - (4+\frac{n}{2})\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [1, 2^n] \textit{ with integer } n >= 0 \textit { and } \exp(-x) \textit{ is normal}$$ + +This formula is obtained for the computation of + +``` +if X < 0 + Y = (exp(2X) - 1) / (exp(2X) + 1) +else + Y = (1 - exp(-2X)) / (1 + exp(-2X)) +``` + +The bounds are tighter than the following computations + +``` + Y = (exp(2X) - 1) / (exp(2X) + 1) +``` + +for which the accuracy would be bound by + +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + 8(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - 4\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right)\textit{ if } x\in [-1, 0]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + (8+n)(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - (4+\frac{n}{2})\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right) \textit{ if } x\in [-2^n, -1] \textit{ with integer } n >= 0 \textit { and } \exp(x) \textit{ is normal}$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + \textcolor{red}{9}(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - \textcolor{red}{4.5}\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{-2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [0, 1]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + (\textcolor{red}{9}+n)(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - (\textcolor{red}{4.5}+\frac{n}{2})\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [1, 2^n] \textit{ with integer } n >= 0 \textit { and } \exp(-x) \textit{ is normal}$$ + +and + +``` + Y = (1 - exp(-2X)) / (1 + exp(-2X)) +``` + +for which the accuracy would be bound by + +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + \textcolor{red}{9}(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - \textcolor{red}{4.5}\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right)\textit{ if } x\in [-1, 0]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + (\textcolor{red}{9}+n)(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - (\textcolor{red}{4.5}+\frac{n}{2})\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right) \textit{ if } x\in [-2^n, -1] \textit{ with integer } n >= 0 \textit { and } \exp(x) \textit{ is normal}$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + 8(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - 4\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{-2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [0, 1]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + (8+n)(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - (4+\frac{n}{2})\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [1, 2^n] \textit{ with integer } n >= 0 \textit { and } \exp(-x) \textit{ is normal}$$ + +and + +``` + Y = (exp(X) - exp(-X)) / (exp(X) + exp(-X)) +``` + +for which the accuracy would be bound by + +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + \textcolor{red}{17}(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - \textcolor{red}{8.5}\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{2x}}\times(1+\textit{\textbf{u}}) + 1 - e^{2x}\right)\textit{ if } x\in [-1, 0]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{2x}} \left(\frac{2 + (\textcolor{red}{17}+n)(1+\textit{\textbf{u}})e^{2x}-2e^{4x}}{(1 + e^{2x})\times(1-\textit{\textbf{u}}) - (\textcolor{red}{8.5}+\frac{n}{2})\textit{\textbf{u}}(1 + \textit{\textbf{u}})e^{2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{2x}\right) \textit{ if } x\in [-2^n, -1] \textit{ with integer } n >= 0 \textit { and } \exp(x) \textit{ is normal}$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + \textcolor{red}{17}(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - \textcolor{red}{8.5}\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [0, 1]$$ +$$|Y_{\textit{err}}^{\textit{intro}}[I]| \leq \frac{\textit{\textbf{u}}}{1+2e^{-2x}} \left(\frac{2 + (\textcolor{red}{17}+n)(1+\textit{\textbf{u}})e^{-2x}-2e^{-4x}}{(1 + e^{-2x})\times(1-\textit{\textbf{u}}) - (\textcolor{red}{8.5}+\frac{n}{2})\textit{\textbf{u}}(1+\textit{\textbf{u}})e^{-2x}}\times(1 + \textit{\textbf{u}}) + 1 - e^{-2x}\right) \textit{ if } x\in [1, 2^n] \textit{ with integer } n >= 0 \textit { and } \exp(-x) \textit{ is normal}$$ + +An implementation can nevertheless use one of these algorithm and verify the adequate accuracy formula, but it should motivate its choice. +Note that the test `if X < 0` may induce some timing penalties for some architecture and the following implementation avoids it + +``` + Y = (1 - exp(-2*abs(X)) / (1 + exp(-2*abs(X))) +``` + +## Unit Verification + +This section contains a test scenario to verify the above specification for any C/C++ implementation. It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. `SymbolicDomainError` is a data structure with 4 fields: + +- The `real` field is a symbolic abstract domain for ideal (infinitely precise) C/C++ floating-point (or fixed-point) computations. +- The `float` field is a symbolic abstract domain for the computed value. +- The `err` field is a symbolic abstract domain for the absolute error, that is the difference between the possible values of `float` and `real`. +- The `rel_err` field is a symbolic abstract domain for the relative error, that is the difference between the possible values of `float` and `real` divided by `real`. + +```c++ +Tensor X; + +/* X symbolic initialization */ + +auto tanh_real = [](const SymbolicDomainError &v) { + // Tanh in the real domain + SymbolicDomainError r; + r.real = std::tanh(v.real); + // float/err/rel_err are set by the abstract interpreter / analysis framework + return r; +}; + +auto result = [&X,&tanh_real](auto I) { + return tanh_real(X[I]); +}; + +for (auto I : X.indexes()) { + auto x = X[I]; + auto y = result(I); + + // First-order propagated error bound: + // |err_tanh| <= |1 - tanh(x)^2| * |err_x| + double tanh_x = std::tanh(x.real); + double local_lipschitz = std::abs(1.0 - tanh_x * tanh_x); + double bound = local_lipschitz * std::abs(x.err); + + assert(std::abs(y.err) <= bound + 1e-12); +} +``` + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/reviews/unsqueeze_review_2025_12_18.md b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/reviews/unsqueeze_review_2025_12_18.md new file mode 100644 index 00000000..cf321269 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/reviews/unsqueeze_review_2025_12_18.md @@ -0,0 +1,433 @@ +# Contents + +- **unsqueeze** operator for type [real](#real) +> We have two arguments, one is real, the other in integer. +> **unsqueeze** operator for type [real,integer](#real_integer) +> Beware of the link... + +- **unsqueeze** operator for type [BFLOAT16, FP16, FP32, FP64, INT2, INT4, INT8, INT16, INT32, INT64, UINT2, UINT4, UINT8, UINT16, UINT32, UINT64, BOOL, STRING](#types) + +> See notation for types: int32, float32, etc. +> See the list of types supported by SONNX (see guidelines). For instance, we don't support bfloat16... + +Based on ONNX documentation version 25. + + +# **Unsqueeze** (real, integer) + +## Signature +Definition of operator $\text{Unsqueeze}$ signature: $Y = \text{Unsqueeze}(X, A)$ + +> *If you have time*, please use notation **Unsqueeze** instead of \text{Unsqueeze}, see guidelines. + +where: +- $X$: input tensor +- $A$: axes tensor (1D Tensor) +- $Y$: output tensor + +## Restrictions +The following restrictions apply to the $\text{Unsqueeze}$ operator for the SONNX profile: + +[General restrictions](../general_restrictions.md) are applicable. + +## Informal specification + +> coordinate => index + +Operator $\text{Unsqueeze}$ inserts single-dimensional entries to the shape of an input tensor $X$ at the specified axes $A$, producing an output tensor $Y$. The rank of the output tensor $Y$ is equal to the rank of the input tensor $X$ plus the number of axes specified in $A$. +$$ +Y[j_0, j_1, \ldots, j_{rY-1}] = X[i_0, i_1, \ldots, i_{rX-1}] +$$ + +where: +- $rY$ = $rA$ + $rX$ + +The mapping between output coordinates $(j_0, j_1, \ldots, j_{rY-1})$ and input coordinates $(i_0, i_1, \ldots, i_{rX-1})$ is defined as follows: + + +> We have to express the relation between the values of the input and output tensors X(index in X) = Y(index in Y) +> X[i_0,...,]=Y[j_0,j_1,...]$ where $j_k = 0$ for the axes that have been added... + +For each output coordinate $(j_0, j_1, \ldots, j_{rY-1})$, the corresponding input coordinate is obtained by removing all $j_p$ where $p \in A'$: + +$$ +(i_0, i_1, \ldots, i_{rX-1}) = (j_p : p \notin A') +$$ + +> $(j_p = 0 : p \in A')$ + +That is, for each $p \notin A'$, $j_p$ maps to the corresponding $i_q$ in $X$, preserving the order. + + +where +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$ + +> We must describe here (and as constraint, later...) how the shape of $Y$ is obtained (addition of axes with dimension 1)? + + +### Example 1 + +> Remark for the WG: the way tensors are displayed has to be explained. It not really homogeneous since 2D matrices are represented without brackets... Should we use Python notation? => to be discussed and formalized. + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0 \end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 2 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +\text{axis} = \begin{bmatrix} -1 \end{bmatrix} +``` +```math +Y = \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix}0 \end{bmatrix} & \begin{bmatrix}1 \end{bmatrix} & \begin{bmatrix}2 \end{bmatrix} & \begin{bmatrix}3 \end{bmatrix} \\ + \begin{bmatrix}4 \end{bmatrix} & \begin{bmatrix}5 \end{bmatrix} & \begin{bmatrix}6 \end{bmatrix} & \begin{bmatrix}7 \end{bmatrix} \\ + \begin{bmatrix}8 \end{bmatrix} & \begin{bmatrix}9 \end{bmatrix} & \begin{bmatrix}10 \end{bmatrix} & \begin{bmatrix}11 \end{bmatrix} + \end{bmatrix} + \quad + \begin{bmatrix} + \begin{bmatrix}12 \end{bmatrix} & \begin{bmatrix}13 \end{bmatrix} & \begin{bmatrix}14 \end{bmatrix} & \begin{bmatrix}15 \end{bmatrix} \\ + \begin{bmatrix}16 \end{bmatrix} & \begin{bmatrix}17 \end{bmatrix} & \begin{bmatrix}18 \end{bmatrix} & \begin{bmatrix}19 \end{bmatrix} \\ + \begin{bmatrix}20 \end{bmatrix} & \begin{bmatrix}21 \end{bmatrix} & \begin{bmatrix}22 \end{bmatrix} & \begin{bmatrix}23 \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 3 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0, 1 \end{bmatrix} +``` +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` + + +> axis => A +> Add an example that adds a dimension not at the bounds. + +## Error conditions +No error condition + +## Inputs + +### $X$: `real tensor` +Tensor $X$ is the input tensor to be unsqueezed. + +### Constraints +Tensor $X$ has no constraints. + +### $A$: `integer tensor` +Tensor $A$ is a 1D tensor containing the axes at which to insert singleton dimensions + +> We have to define how axes can be designated either using positive or negative values. The definition given in ONNX is unclear: "counting from the back" does not really matches the intuitive way of counting... This should be defined in file [definition.md](../common/definitions.md). For instance, we could introduce the concept "extended index"... +> Tensor A is a 1D tensor containing the [extended indexes](#...) at which... + + +### Constraints + + - `[C1]` Value Domain + - Statement: + $$\forall i \in [0, rA-1], \; A[i] \in [-rY, rY-1]$$ + + - `[C2]` Uniqueness + - Statement: After normalizing negative indices, all axes in tensor $A$ shall be unique. + $$\forall i, j \in [0, rA-1], \; (A[i] + rY) \bmod rY = ((A[j] + rY) \bmod rY)\implies (i = j)$$ + - Rationale: Prevents ambiguity when the same axis is specified multiple times using different representations (negative and positive). + +> Replace statement: "an axis shall only be designated once in A" does not refer to normalization (or define it first). + + +> Sorry... so sorry... the guidleines have changed and now section "Attributes" is placed before the "inputs" section. +> +## Attributes +Operator $\text{Unsqueeze}$ has no attributes. + +## Outputs + +### $Y$: `real tensor` +Tensor $Y$ is the output tensor after unsqueezing tensor $X$ at the specified axes $A$. + +> replace: "at the axes designated by $A$" + +### Constraints + + - `[C1]` Shape consistency + - Statement: +$$ +Y = (d'_0, d'_1, \ldots, d'_{rY-1}) +$$ + +> We designate sizes with $dY_0, dY_1,...$ so the definition of the shape should use constraints on $dY_i$. + +Where: +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$. +- For each $p \in [0, rY-1]$: + - If $p \in A'$, then $d'_p = 1$ + - If $p \notin A'$, then $d'_p = d_q$, where $q$ is the index corresponding to the original dimension of $X$ + +> Last bullet to be replaced by: +$$(dX_0, dX_1, \ldots, dX_{rX-1}) = (dY_p : p \notin A')$$ + + +> Remove "original" +> $d_q$ => $dX_q$ + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +The $\text{Unsqueeze}$ operator does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. + + + + +# **Unsqueeze** (type, itype) +where +- type is in: { BFLOAT16, FP16, FP32, FP64, INT2, INT4, INT8, INT16, INT32, INT64, UINT2, UINT4, UINT8, UINT16, UINT32, UINT64, BOOL, STRING } + +- itype is in: { INT64 } + +## Signature +Definition of operator $\text{Unsqueeze}$ signature: $Y = \text{Unsqueeze}(X, A)$ + +where: +- $X$: input tensor +- $A$: axes tensor (1D Tensor) +- $Y$: output tensor + +> As this is operator operates on the structure the semantics does not depends on the type so we could use the following sentence: "See specification for real numbers." and remove the rest (see operator "Abs"). + + +## Restrictions +The following restrictions apply to the $\text{Unsqueeze}$ operator for the SONNX profile: + +[General restrictions](../general_restrictions.md) are applicable. + +## Informal specification + +Operator $\text{Unsqueeze}$ inserts single-dimensional entries to the shape of an input tensor $X$ at the specified axes $A$, producing an output tensor $Y$. The rank of the output tensor $Y$ is equal to the rank of the input tensor $X$ plus the number of axes specified in $A$. +$$ +Y[j_0, j_1, \ldots, j_{rY-1}] = X[i_0, i_1, \ldots, i_{rX-1}] +$$ + +where: +- $rY$ = $rA$ + $rX$ + +The mapping between output coordinates $(j_0, j_1, \ldots, j_{rY-1})$ and input coordinates $(i_0, i_1, \ldots, i_{rX-1})$ is defined as follows: + + +For each output coordinate $(j_0, j_1, \ldots, j_{rY-1})$, the corresponding input coordinate is obtained by removing all $j_p$ where $p \in A'$: + +$$ +(i_0, i_1, \ldots, i_{rX-1}) = (j_p : p \notin A') +$$ + +That is, for each $p \notin A'$, $j_p$ maps to the corresponding $i_q$ in $X$, preserving the order. + +where +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$ + +### Example 1 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0 \end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 2 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +\text{axis} = \begin{bmatrix} -1 \end{bmatrix} +``` +```math +Y = \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix}0 \end{bmatrix} & \begin{bmatrix}1 \end{bmatrix} & \begin{bmatrix}2 \end{bmatrix} & \begin{bmatrix}3 \end{bmatrix} \\ + \begin{bmatrix}4 \end{bmatrix} & \begin{bmatrix}5 \end{bmatrix} & \begin{bmatrix}6 \end{bmatrix} & \begin{bmatrix}7 \end{bmatrix} \\ + \begin{bmatrix}8 \end{bmatrix} & \begin{bmatrix}9 \end{bmatrix} & \begin{bmatrix}10 \end{bmatrix} & \begin{bmatrix}11 \end{bmatrix} + \end{bmatrix} + \quad + \begin{bmatrix} + \begin{bmatrix}12 \end{bmatrix} & \begin{bmatrix}13 \end{bmatrix} & \begin{bmatrix}14 \end{bmatrix} & \begin{bmatrix}15 \end{bmatrix} \\ + \begin{bmatrix}16 \end{bmatrix} & \begin{bmatrix}17 \end{bmatrix} & \begin{bmatrix}18 \end{bmatrix} & \begin{bmatrix}19 \end{bmatrix} \\ + \begin{bmatrix}20 \end{bmatrix} & \begin{bmatrix}21 \end{bmatrix} & \begin{bmatrix}22 \end{bmatrix} & \begin{bmatrix}23 \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 3 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0, 1 \end{bmatrix} +``` +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +## Error conditions +No error condition + +## Inputs + +### $X$: `type tensor` +Tensor $X$ is the input tensor to be unsqueezed. + +### Constraints + - `[C1]` Type consistency + - Statement: Tensors $X$ and $Y$ must have the same data type. + +### $A$: `itype tensor` +Tensor $A$ is a 1D tensor containing the axes at which to insert singleton dimensions + +### Constraints + + - `[C1]` Value Domain + - Statement: + $$\forall i \in [0, rA-1], \; A[i] \in [-rY, rY-1]$$ + + - `[C2]` Uniqueness + - Statement: After normalizing negative indices, all axes in tensor $A$ shall be unique. + $$\forall i, j \in [0, rA-1], \; (A[i] + rY) \bmod rY = ((A[j] + rY) \bmod rY)\implies (i = j)$$ + - Rationale: Prevents ambiguity when the same axis is specified multiple times using different representations (negative and positive). + + +## Attributes +Operator $\text{Unsqueeze}$ has no attributes. + +## Outputs + +### $Y$: `type tensor` +Tensor $Y$ is the output tensor after unsqueezing tensor $X$ at the specified axes $A$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: +$$ +Y = (d'_0, d'_1, \ldots, d'_{rY-1}) +$$ + +Where: +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$. +- For each $p \in [0, rY-1]$: + - If $p \in A'$, then $d'_p = 1$ + - If $p \notin A'$, then $d'_p = d_q$, where $q$ is the index corresponding to the original dimension of $X$ + + - `[C2]` Type consistency + - Statement: see constraint [[C1]](#C1ta) on tensor $X$. + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +The $\text{Unsqueeze}$ operator does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/READEME.md b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/READEME.md new file mode 100644 index 00000000..5c5f7700 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/READEME.md @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_unsqueeze.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_unsqueeze.py you need to do +```bash +pytest test_unsqueeze.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_unsqueeze.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_unsqueeze.py + +# Explanations of Unsqueeze corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Unsqueeze tests +For the unsqueeze operator we just **checked both the corner and edge cases** for the **shape_size_input_x**, **size_input_x**, **size_input_axes** and **x_type** parameters. + +We consider **shape_size_input_x**, **size_input_x**, **size_input_axes** and **x_type** as **lines** since they are independent and we checked the corner and edge cases for all of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/check_edges.py new file mode 100644 index 00000000..442f6b09 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/check_edges.py @@ -0,0 +1,120 @@ +import json +import numpy as np +import ml_dtypes + + +unsqueeze_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "STRING": np.str_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r") as f: + data = json.load(f) + check_shape_size_input_x = check_individual_variables( + "shape_size_input_x", + data["shape_size_input_x"], + data["min_shape_size_input_x"], + data["max_shape_size_input_x"] + ) + size_input_x_axis = [ item for sublist in data["size_input_x"] for item in sublist ] + check_size_input_x = check_individual_variables( + "size_input_x", + size_input_x_axis, + data["min_size_input_x"], + data["max_size_input_x"] + ) + check_size_input_axes = check_individual_variables( + "size_input_axes", + data["size_input_axes"], + data["min_number_of_axes"], + data["max_number_of_axes"] + ) + provider = data["ONNXRuntime_Provider"] + check_x_type = check_type("x_type", data["x_type"], provider) + + return all([check_shape_size_input_x, check_size_input_x, check_size_input_axes, check_x_type]) + + +""" +Check individual variable analysis +""" +def check_individual_variables(variable_name, variable_analysis, min, max): + corner_cases = [min, max] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + + has_mean_case_mean = any(d for d in variable_analysis if d > min and d < max) + has_out_of_bounds = not(any(d for d in variable_analysis if d < min or d > max)) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(f" - Missing mean case") + if not has_out_of_bounds: + print(f" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(unsqueeze_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + +check_edges() \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/test_unsqueeze.py b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/test_unsqueeze.py new file mode 100644 index 00000000..020a38d9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/hypothesis/test_unsqueeze.py @@ -0,0 +1,298 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Unsqueeze operation in ONNX. +""" +import os + +import json +import numpy as np + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes details +""" +inputs_attributes = { + "min_shape_size_input_x": 0, # Adjust as needed + "max_shape_size_input_x": 4, # Adjust as needed + "min_size_input_x_axis": 0, # Adjust as needed ( Minimum 0 ) + "max_size_input_x_axis": 10, # Adjust as needed + "min_number_of_axes": 0, # Adjust as needed ( Minimum 0 ) + "max_number_of_axes": 4, # Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + +""" +Unsqueeze supported types +""" +unsqueeze_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "STRING": np.str_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +""" +Store generated data +""" +generated_data = { + "shape_size_input_x" : [], + "size_input_x": [], + "size_input_axes": [], + "x_type": [] +} + +dtype_to_key = {v: k for k, v in unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Function to generate valid unsqueeze arguments +""" +@st.composite +def valid_unsqueeze_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + # X [C1] - Type Consistency + all_valid_types = list(unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + dtype_name = draw(st.sampled_from(all_valid_types)) + dtype = unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"])[dtype_name] + + if np.issubdtype(dtype, np.integer): + min_value = np.iinfo(dtype).min + max_value = np.iinfo(dtype).max + a_numeric = st.integers(min_value=min_value, max_value=max_value) + elif np.issubdtype(dtype, np.floating): + min_value = np.finfo(dtype).min + max_value = np.finfo(dtype).max + a_numeric = st.floats(min_value=min_value, max_value=max_value) + elif np.issubdtype(dtype, np.bool_): + a_numeric = st.booleans() + elif np.issubdtype(dtype, np.str_): + a_numeric = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + + # shape_size_input_x is the number of dimensions of input tensor X + shape_size_input_x = draw(st.integers( + min_value=inputs_attributes["min_shape_size_input_x"], + max_value=inputs_attributes["max_shape_size_input_x"])) + + # x_shape is the shape of input tensor X + x_shape = [] + for _ in range(shape_size_input_x): + dim = draw(st.integers( + min_value=inputs_attributes["min_size_input_x_axis"], + max_value=inputs_attributes["max_size_input_x_axis"])) + x_shape.append(dim) + + # Create input tensor X + # X [C1] + x = draw(hnp.arrays(dtype=dtype, shape=x_shape, elements=a_numeric)) + + #--------------------------------------------------- + # Input A + #--------------------------------------------------- + + # number_of_axes is the number of axes to unsqueeze + number_of_axes = draw(st.integers( + min_value=inputs_attributes["min_number_of_axes"], + max_value=inputs_attributes["max_number_of_axes"])) + + # r is the rank of output tensor Y + r = shape_size_input_x + number_of_axes + # all_valid_axes is the list of all non negative axes to unsqueeze + # A [C1], # A [C2] + all_valid_axes = list(range(r)) + # axes is the list of axes to unsqueeze + axes_values = draw(st.permutations(all_valid_axes))[:number_of_axes] + + # Create input tensor A + a = [] + for a_val in axes_values: + # Randomly decide to use negative or positive axis representation + use_negative = draw(st.booleans()) + if use_negative: + a.append(a_val - r) + else: + a.append(a_val) + a = np.array(a, dtype=np.int64) + a_normalized = np.where(a < 0, a + r, a) + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + + # Y [C1] + output_shape = list(x.shape) + for axis in sorted(a_normalized): + pos = axis if axis >= 0 else len(output_shape) + axis + 1 + output_shape.insert(pos, 1) + + return x, a, output_shape, dtype_name + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_unsqueeze_args()) +def test_unsqueeze(args): + x, a, output_shape, dtype_name = args + x_type_key = dtype_to_key.get(x.dtype.type, str(x.dtype)) + generated_data["x_type"].append(x_type_key) + generated_data["shape_size_input_x"].append(len(x.shape)) + generated_data["size_input_x"].append(x.shape) + generated_data["size_input_axes"].append(len(a)) + + y = run_onnx_unsqueeze(x, a, output_shape, dtype_name) + check_constraints(x, a, y, output_shape) + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated for testing ONNX Unsqueeze Operator", + "min_shape_size_input_x": inputs_attributes["min_shape_size_input_x"], + "max_shape_size_input_x": inputs_attributes["max_shape_size_input_x"], + "shape_size_input_x": generated_data["shape_size_input_x"], + "min_size_input_x": inputs_attributes["min_size_input_x_axis"], + "max_size_input_x": inputs_attributes["max_size_input_x_axis"], + "size_input_x": generated_data["size_input_x"], + "min_number_of_axes": inputs_attributes["min_number_of_axes"], + "max_number_of_axes": inputs_attributes["max_number_of_axes"], + "size_input_axes": generated_data["size_input_axes"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"], + "x_type": generated_data["x_type"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def run_onnx_unsqueeze(x, a, output_shape, dtype_name): + """ + Function that runs the ONNX Unsqueeze operation + """ + + # Create inputs + x_onnx = helper.make_tensor_value_info('x', + helper.np_dtype_to_tensor_dtype(x.dtype), + x.shape) + a_onnx = helper.make_tensor_value_info('a', + helper.np_dtype_to_tensor_dtype(a.dtype), + a.shape) + + node_def = helper.make_node( + 'Unsqueeze', + ['x', 'a'], + ['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-unsqueeze', + [x_onnx, a_onnx], + # Y [C2] + [helper.make_tensor_value_info('y', + helper.np_dtype_to_tensor_dtype(x.dtype), + output_shape)], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + y = sess.run(None, {'x': x, 'a': a})[0] + + print("x shape:", x.shape) + print("a values:", a) + print("y shape:", y.shape) + return y + +def check_constraints(x, a, y, output_shape): + """ + Function that defines asserts for the constraints + """ + + # X[C1], Y[C2] -> X[C1] + x_is_string = np.issubdtype(x.dtype, np.str_) or np.issubdtype(x.dtype, np.object_) + y_is_string = np.issubdtype(y.dtype, np.str_) or np.issubdtype(y.dtype, np.object_) + if x_is_string and y_is_string: + pass + else: + assert x.dtype == y.dtype + # A [C1] + r = len(y.shape) + assert np.all((a >= -r) & (a < r)) + # A [C2] + a_normalized = (a + r) % r + assert len(np.unique(a_normalized)) == len(a_normalized) + # Y [C1] + assert list(y.shape) == output_shape + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.ipynb b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.ipynb new file mode 100644 index 00000000..8a054838 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "245013fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "input_axis_name = \"A\"\n", + "unsqueeze_output_name = \"Y\"\n", + "\n", + "# Create the ONNX model with Unsqueeze operator\n", + "def create_unsqueeze_model(axis, input_rank, output_shape, dtype, axis_dtype):\n", + "\n", + " #Create input tensor\n", + " input_x = onnx.helper.make_tensor_value_info(input_name, dtype, input_rank)\n", + " input_axis = onnx.helper.make_tensor_value_info(input_axis_name, axis_dtype, [len(axis)])\n", + "\n", + " # Create output tensor (final result after unsqueeze operation)\n", + " unsqueeze_output = onnx.helper.make_tensor_value_info(unsqueeze_output_name, dtype, output_shape)\n", + "\n", + " # Define unsqueeze node\n", + " unsqueeze_node = onnx.helper.make_node(\n", + " \"Unsqueeze\",\n", + " inputs=[input_name, input_axis_name],\n", + " outputs=[unsqueeze_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [unsqueeze_node],\n", + " \"Unsqueeze\",\n", + " [input_x, input_axis],\n", + " [unsqueeze_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)])\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"unsqueeze.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"unsqueeze.onnx\")\n", + " return session\n", + "\n", + "def do_unsqueeze(x, axis, session):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x, input_axis_name: axis})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(\"Shape of input tensor:\", x.shape)\n", + " print(f\"X={x_f}\")\n", + " print(\"Shape of output tensor:\", output[0].shape)\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c66a3e9a", + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_output_shape(x, a):\n", + " r = len(x.shape) + len(a)\n", + " a_normalized = []\n", + " for a_value in a:\n", + " if a_value < 0:\n", + " a_normalized.append(a_value + r)\n", + " else:\n", + " a_normalized.append(a_value)\n", + "\n", + " output_shape = list(x.shape)\n", + " for axis in sorted(a_normalized):\n", + " pos = axis if axis >= 0 else len(output_shape) + axis + 1\n", + " output_shape.insert(pos, 1)\n", + " return output_shape" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fda05307", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1, 2, 3, 4)\n", + "Result = [[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]]\n" + ] + } + ], + "source": [ + "# Case N1: 3-rank tensor (int32), axes = [0]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([0]).astype(np.int64) \n", + "input_shape = list(x.shape)\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b9682e0a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (2, 3, 4, 1)\n", + "Result = [[[[ 0], [ 1], [ 2], [ 3]], [[ 4], [ 5], [ 6], [ 7]], [[ 8], [ 9], [10], [11]]], [[[12], [13], [14], [15]], [[16], [17], [18], [19]], [[20], [21], [22], [23]]]]\n" + ] + } + ], + "source": [ + "# Case N2: 3-rank tensor (int32), axes = [-1]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([-1]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "43e09eae", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1, 1, 2, 3, 4)\n", + "Result = [[[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]]]\n" + ] + } + ], + "source": [ + "# Case N3: 3-rank tensor (int32), axes = [0,1]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([0,1]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "388ac5b2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2025-12-10 11:57:46.491544509 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m 7\u001b[39m output_shape = calculate_output_shape(x, axes)\n\u001b[32m 8\u001b[39m session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43mdo_unsqueeze\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43maxes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 49\u001b[39m, in \u001b[36mdo_unsqueeze\u001b[39m\u001b[34m(x, axis, session)\u001b[39m\n\u001b[32m 47\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_unsqueeze\u001b[39m(x, axis, session):\n\u001b[32m 48\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m49\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_axis_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 51\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 52\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venvs/hypothesis-env/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n" + ] + } + ], + "source": [ + "# This cell must fail. The axis value is out of range\n", + "# Case N4: 3-rank tensor (int32), axes = [4]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([4]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96a1ee35", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.onnx b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.onnx new file mode 100644 index 00000000..ad6b075b Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/tests/unsqueeze.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/unsqueeze.md b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/unsqueeze.md new file mode 100644 index 00000000..efbd1ecb --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/unsqueeze/unsqueeze.md @@ -0,0 +1,254 @@ +# Contents + +- **Unsqueeze** operator for type [real,integer](#real_integer) +- **Unsqueeze** operator for type [float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, bool, string](#type) + +Based on ONNX [Unsqueeze version 25](https://onnx.ai/onnx/operators/onnx__Unsqueeze.html). + + +# **Unsqueeze** (real, integer) + +## Signature +Definition of operator **Unsqueeze** signature: + + +$Y = \textbf{Unsqueeze}(X, A)$ + +where: +- $X$: input tensor +- $A$: axes tensor (1D Tensor) +- $Y$: output tensor + +## Restrictions +The following restrictions apply to the **Unsqueeze** operator for the SONNX profile: + +[General restrictions](../common/general_restrictions.md) are applicable. + +## Informal specification + +Operator **Unsqueeze** inserts single-dimensional entries to the shape of an input tensor $X$ at the specified axes $A$, producing an output tensor $Y$. The rank of the output tensor $Y$ is equal to the rank of the input tensor $X$ plus the number of axes specified in $A$. + +$$ +X[i_0, i_1, \ldots, i_{rX-1}] = Y[j_0, j_1, \ldots, j_{rY-1}] +$$ + +where: +- $rY$ = $rA$ + $rX$ + + +The mapping between input index $(i_0, i_1, \ldots, i_{rX-1})$ and output index $(j_0, j_1, \ldots, j_{rY-1})$ is defined as follows: + + +For each input index $(i_0, i_1, \ldots, i_{rX-1})$, the corresponding output index is obtained by inserting singleton dimensions at the axes specified in $A'$: + +$$ +(j_0, j_1, \ldots, j_{rY-1}) = \forall k \in A'. \quad \text{f (0,k, $(i_0, i_1, \ldots, i_{rX-1})$)} +$$ + +where +- $f$ is a function that inserts 0 at position $k$ in the index ($i_0, i_1, \ldots, i_{rX-1}$) + +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$ + + +To calculate the ouput shape: + +$$ +(dY_0, dY_1, \ldots, dY_{rY-1}) = \forall k \in A'. \quad \text{f (1,k, $(dX_0, dX_1, \ldots, dX_{rX-1})$)} +$$ + +where +- $f$ is a function that inserts 1 at position $k$ in the shape ($dX_0, dX_1, \ldots, dX_{rX-1}$) +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$ + +### Example 1 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0 \end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 2 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` + +```math +\text{axis} = \begin{bmatrix} -1 \end{bmatrix} +``` +```math +Y = \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix}0 \end{bmatrix} & \begin{bmatrix}1 \end{bmatrix} & \begin{bmatrix}2 \end{bmatrix} & \begin{bmatrix}3 \end{bmatrix} \\ + \begin{bmatrix}4 \end{bmatrix} & \begin{bmatrix}5 \end{bmatrix} & \begin{bmatrix}6 \end{bmatrix} & \begin{bmatrix}7 \end{bmatrix} \\ + \begin{bmatrix}8 \end{bmatrix} & \begin{bmatrix}9 \end{bmatrix} & \begin{bmatrix}10 \end{bmatrix} & \begin{bmatrix}11 \end{bmatrix} + \end{bmatrix} + \quad + \begin{bmatrix} + \begin{bmatrix}12 \end{bmatrix} & \begin{bmatrix}13 \end{bmatrix} & \begin{bmatrix}14 \end{bmatrix} & \begin{bmatrix}15 \end{bmatrix} \\ + \begin{bmatrix}16 \end{bmatrix} & \begin{bmatrix}17 \end{bmatrix} & \begin{bmatrix}18 \end{bmatrix} & \begin{bmatrix}19 \end{bmatrix} \\ + \begin{bmatrix}20 \end{bmatrix} & \begin{bmatrix}21 \end{bmatrix} & \begin{bmatrix}22 \end{bmatrix} & \begin{bmatrix}23 \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` +### Example 3 + +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 0, 1 \end{bmatrix} +``` +```math +Y = +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \quad + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` + +### Example 4 +```math +X = \begin{bmatrix} + \begin{bmatrix} 0 & 1 & 2 & 3 \\ 4 & 5 & 6 & 7 \\ 8 & 9 & 10 & 11 \end{bmatrix} + \begin{bmatrix} 12 & 13 & 14 & 15 \\ 16 & 17 & 18 & 19 \\ 20 & 21 & 22 & 23 \end{bmatrix} +\end{bmatrix} +``` +```math +\text{axis} = \begin{bmatrix} 1, 2 \end{bmatrix} +``` + +```math +\begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 0 & 1 & 2 & 3 \\ + 4 & 5 & 6 & 7 \\ + 8 & 9 & 10 & 11 + \end{bmatrix} + \end{bmatrix} + \end{bmatrix} + \quad + \begin{bmatrix} + \begin{bmatrix} + \begin{bmatrix} + 12 & 13 & 14 & 15 \\ + 16 & 17 & 18 & 19 \\ + 20 & 21 & 22 & 23 + \end{bmatrix} + \end{bmatrix} + \end{bmatrix} +\end{bmatrix} +``` + +## Error conditions +No error condition + +## Attributes +Operator **Unsqueeze** has no attributes. + +## Inputs + +### $X$: `real tensor` +Tensor $X$ is the input tensor to be unsqueezed. + +### Constraints +Tensor $X$ has no constraints. + +### $A$: `integer tensor` +Tensor $A$ is a 1D tensor containing the axes at which to insert singleton dimensions. + +Note that any negative index, $a$, represents the corresponding non-negative index calculated as $a + rA$. + +### Constraints + + - `[C1]` Value Domain + - Statement: + $$\forall i \in [0, rA-1], \; A[i] \in [-rY, rY-1]$$ + + - `[C2]` Uniqueness + - Statement: An axis shall only be designated once in A. + $$\forall i, j \in [0, rA-1], \; (A[i] + rY) \bmod rY = ((A[j] + rY) \bmod rY)\implies (i = j)$$ + - Rationale: Prevents ambiguity when the same axis is specified multiple times using different representations (negative and positive). + + +## Outputs + +### $Y$: `real tensor` +Tensor $Y$ is the output tensor after unsqueezing tensor $X$ at the axes designated by +$A$. + +### Constraints + + - `[C1]` Shape consistency + - Statement: +$$ +(dY_0, dY_1, \ldots, dY_{rY-1}) = \forall k \in A'. \quad \text{f (1,k, $(dX_0, dX_1, \ldots, dX_{rX-1})$)} +$$ + +where +- $f$ is a function that inserts 1 at position $k$ in the shape ($dX_0, dX_1, \ldots, dX_{rX-1}$) +- $A'$ is the set of axes specified in tensor $A$, normalized and sorted to be in the range $[0, rY-1]$ + + + +## Formal specification + +See the Why3 specification. + +## Numerical Accuracy + +The $\text{Unsqueeze}$ operator does not introduce any numerical error. Hence, for all valid indices the output values are exactly equal to the corresponding input values. + + + + +# **Unsqueeze** (type, int64) +where type is in: { float16, float, double, int8, int16, int32, int64, uint8, uint16, uint32, uint64, bool, string } + +See specification for [real numbers](#real_integer). + diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/assets/README.md b/safety-related-profile/sonnx/ops/spec/informal/where/assets/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/ONNX_WHERE.png b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/ONNX_WHERE.png new file mode 100644 index 00000000..fa3f5cdc Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/ONNX_WHERE.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio new file mode 100644 index 00000000..75ebebbe --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio.png b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio.png new file mode 100644 index 00000000..31c2f642 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/informal/where/assets/imgs/where.drawio.png differ diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/reviews/eric.md b/safety-related-profile/sonnx/ops/spec/informal/where/reviews/eric.md new file mode 100644 index 00000000..88d0b159 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/where/reviews/eric.md @@ -0,0 +1,31 @@ +- In ONNX, the operator is `Where`, not `WHERE`. (To be checked for `Conv`.) +- Should we generalize the restriction on the usage of [sparse tensors](https://onnx.ai/onnx/intro/concepts.html#sparse-tensor)? +- Put R2 as the first restriction so that R1 and R3 are in sequence. +- Restrictions about sparse tensors and broadcasting, no shape / type inference should probably put in a unique / common document. +- "- `Z`: output tensor based on the `condition`, `X`, and `Y`" => "- `Z`: output tensor" +- "if the corresponding entry" => make "corresponding" more explicit? (to be discussed since there is a mathematical expression just after...) +- "is different of false" => "is True" (to be homogeneous with the previous statements) +- figure + - if the figure has been copied, please indicate the source. + - "Cond" => "condition" + - show the condition tensor on the figure +- examples + - give an example not using Booleans but numerical values +- python + - **To be discussed: should we generalize this (e.g., provide a jupyter notebook with examples)** This could be a good idea... +- Description of condition` + - constraints + - "consistency in shape" => "shape consistency"? + - "N" must be defined and check if it is necessary. +- Description of `X + - Add the type + - "[...] based on the [...]" => "[...] based on [...]" + - constraint : use a cross-ref to the previous constraint. +- Description of `Y` + - *same remarks as for `X`* +- Description of `Z` + - "The tensor `Z` [...]" => "Tensor `Z` [...]" +- Attributes + - "The where operator does not require any attributes." =?=> "The where operator has no attribute." or "Not applicable". +- Formal specification + - To be removed for the moment. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/where.md b/safety-related-profile/sonnx/ops/spec/informal/where/where.md new file mode 100644 index 00000000..7bde033a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/where/where.md @@ -0,0 +1,485 @@ + + +# Contents + +- **Where** operator for type [real](#real) +- **Where** operator for types [float16, float, double](#float) +- **Where** operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Where version 16](https://onnx.ai/onnx/operators/onnx__Where.html). + + + +# **Where** (bool, real, real) + +## Signature + +$Z = \textbf{Where}(condition, X, Y)$ + +where: + +* $condition$: boolean input tensor used to select values between $X$ and $Y$ +* $X$: first input tensor +* $Y$: second input tensor +* $Z$: output tensor formed by element-wise selection between $X$ and $Y$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Where** operator. + +## Informal specification + +Operator **Where** selects elements from tensors $X$ and $Y$ element-wise according to the boolean tensor $condition$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +> Image en fond blanc. + +$$ +Z[i] = +\begin{cases} +X[i] & \text{if } condition[i] = \text{True} \\ +Y[i] & \text{otherwise} +\end{cases} +$$ + +The effect of the operator is illustrated on the following examples. +The following figure illustrates operator **Where** applied on inputs $X$,$Y$ with condition $condition$ : + +drawing + +### Example 1 + +```math +condition = \begin{bmatrix} \text{True} & \text{False} & \text{True} \end{bmatrix} +``` + +```math +X = \begin{bmatrix} 9.0 & 8.0 & 7.1 \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 6.0 & 5.0 & 4.0 \end{bmatrix} +``` + +Result $Z$ is: + +```math +Z = \begin{bmatrix} 9.0 & 5.0 & 7.1 \end{bmatrix} +``` + +### Example 2 + +```math +condition = +\begin{bmatrix} +\text{True} & \text{True} \\ +\text{True} & \text{False} \\ +\text{False} & \text{True} +\end{bmatrix} +``` + +```math +X = +\begin{bmatrix} +1 & 2 \\ +3 & 4 \\ +5 & 6 +\end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} +12 & 11 \\ +10 & 9 \\ +8 & 7 +\end{bmatrix} +``` + +Result $Z$ is: + +```math +Z = +\begin{bmatrix} +1 & 2 \\ +3 & 9 \\ +8 & 6 +\end{bmatrix} +``` + +## Error conditions + +No error condition. + +## Attributes + +Operator **Where** has no attribute. + +## Inputs + +### $\text{condition}$: bool tensor + +Boolean tensor used to select values from $X$ and $Y$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $condition$, $X$, $Y$, and $Z$ shall have the same shape. + +### $\text{X}$: real tensor + +First input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [`[C1]`](#C1ra) on tensor $condition$. + + + +### $\text{Y}$: real tensor + +Second input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [`[C1]`](#C1ra) on tensor $condition$. + +## Outputs + +### $\text{Z}$: real tensor + +Output tensor formed by selecting values from $X$ and $Y$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: See constraint [`[C1]`](#C1ra) on tensor $condition$. + +## Formal specification + +See the Why3 specification. + + + + + +# **Where** (bool, float, float) + +where float is in {float16, float, double} + +## Signature + +Definition of operator $\text{Where}$ signature: +$Z = \textbf{Where}(condition, X, Y)$ + +where: + +- $condition$: boolean input tensor +- $X$: first floating-point input tensor +- $Y$: second floating-point input tensor +- $Z$: output tensor formed by element-wise selection + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Where** operator. + +## Informal specification + +Operator **Where** selects elements from tensors $X$ and $Y$ element-wise according to the boolean tensor $condition$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Z[i] = +\begin{cases} +X[i] & \text{if } condition[i] = \text{True} \\ +Y[i] & \text{otherwise} +\end{cases} +$$ + +Selection follows standard IEEE 754 semantics for floating-point values. +In particular, NaN and infinite values are propagated according to the selected branch. + + +### Example 1 + +```math +condition = \begin{bmatrix} \text{True} & \text{False} & \text{True} \end{bmatrix} +``` + +```math +X = \begin{bmatrix} 19.0 & 28.0 & 37.1 \end{bmatrix} +``` + +```math +Y = \begin{bmatrix} 16.0 & 25.0 & 34.0 \end{bmatrix} +``` + +Result $Z$ is: + +```math +Z = \begin{bmatrix} 19.0 & 25.0 & 37.1 \end{bmatrix} +``` + + + +### Example 2 + +```math +condition = +\begin{bmatrix} +\text{True} & \text{False} & \text{True} & \text{False} & \text{True} +\end{bmatrix} +``` + +```math +X = +\begin{bmatrix} +\text{+0.0} & \text{+0.0} & \text{+inf} & \text{+inf} & \text{NaN} +\end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} +\text{+0.0} & \text{-0.0} & \text{-inf} & \text{-inf} & 1.0 +\end{bmatrix} +``` + +Result $Z$ is: + +```math +Z = +\begin{bmatrix} +\text{+0.0} & \text{-0.0} & \text{+inf} & \text{-inf} & \text{NaN} +\end{bmatrix} +``` + + + +## Error conditions + +No error condition. + +## Attributes + +Operator **Where** has no attribute. + +## Inputs + +### $\text{condition}$: bool tensor + +Boolean tensor used for selection. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $condition$, $X$, $Y$, and $Z$ shall have the same shape. +- `[C2]` Type consistency + + - Statement: Tensors $X$, $Y$, and $Z$ shall have the same floating-point type. + + + +### $\text{X}$: floating-point tensor + +First input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + * Statement: see constraint [`[C1]`](#C1fa) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: see constraint [`[C2]`](#C2fa) on tensor $condition$. + + + +### $\text{Y}$: floating-point tensor + +Second input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [`[C1]`](#C1fa) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: see constraint [`[C2]`](#C2fa) on tensor $condition$. + +## Outputs + +### $\text{Z}$: floating-point tensor + +Output tensor formed by selection between $X$ and $Y$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: See constraint [`[C1]`](#C1fa) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: See constraint [`[C2]`](#C2fa) on tensor $condition$. + +## Formal specification + +See Why3 specification. + +## Numeric accuracy + +[See the numeric accuracy note](./where_acc.md). + + + + +# **Where** (bool, int, int) + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64} + +## Signature + +Definition of operator $\text{Where}$ signature: +$Z = \textbf{Where}(condition, X, Y)$ + +where: + +- $condition$: boolean input tensor +- $X$: first integer input tensor +- $Y$: second integer input tensor +- $Z$: output tensor formed by element-wise selection + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Where** operator. + +## Informal specification + +Operator **Where** selects elements from integer tensors $X$ and $Y$ element-wise according to the boolean tensor $condition$. + +For any [tensor index](./../common/definitions.md#tensor_index) $i$: + +$$ +Z[i] = +\begin{cases} +X[i] & \text{if } condition[i] = \text{True} \\ +Y[i] & \text{otherwise} +\end{cases} +$$ + +The examples given in the real section apply directly when restricted to integer values. +### Example 1 + +```math +X = +\begin{bmatrix} +1 & 20 \\ +3 & 40 \\ +5 & 60 +\end{bmatrix} +``` + +```math +Y = +\begin{bmatrix} +12 & 110 \\ +10 & 90 \\ +8 & 70 +\end{bmatrix} +``` + +Result $Z$ is: + +```math +Z = +\begin{bmatrix} +1 & 20 \\ +3 & 90 \\ +8 & 60 +\end{bmatrix} +``` +## Error conditions + +No error condition. + +## Attributes + +Operator **Where** has no attribute. + +## Inputs + +### $\text{condition}$: bool tensor + +Boolean tensor used for selection. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: Tensors $condition$, $X$, $Y$, and $Z$ shall have the same shape. +- `[C2]` Type consistency + + - Statement: Tensors $X$, $Y$, and $Z$ shall have the same integer type. + + + +### $\text{X}$: integer tensor + +First input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [`[C1]`](#C1ia) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: see constraint [`[C2]`](#C2ia) on tensor $condition$. + + + +### $\text{Y}$: integer tensor + +Second input tensor. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: see constraint [`[C1]`](#C1ia) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: see constraint [`[C2]`](#C2ia) on tensor $condition$. + +## Outputs + +### $\text{Z}$: integer tensor + +Output tensor formed by selection between $X$ and $Y$. + +#### Constraints + +- `[C1]` Shape consistency + + - Statement: See constraint [`[C1]`](#C1ia) on tensor $condition$. +- `[C2]` Type consistency + + - Statement: See constraint [`[C2]`](#C2ia) on tensor $condition$. + + diff --git a/safety-related-profile/sonnx/ops/spec/informal/where/where_acc.md b/safety-related-profile/sonnx/ops/spec/informal/where/where_acc.md new file mode 100644 index 00000000..71a77196 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/informal/where/where_acc.md @@ -0,0 +1,104 @@ +# Numerical Accuracy + +$Z_{\textit{err}} = Z_{\textit{err}}^{\textit{propag}} + Z_{\textit{err}}^{\textit{intro}}$. + + + +## Error Propagation - for information - see [guidelines](../../../docs/guidelines/accuracy.md#error-propagation) + +This section contains tight properties of $Z_{\textit{err}}^{\textit{propag}}$, the propagated error, where $Z$ is the tensor result of the **Where** operator. +Let tensors of numerical errors be denoted by subscripts “err” (e.g., $X_{\textit{err}}$). + +For $Z = \textbf{Where}(condition, X, Y)$, the propagated error depends only on the selected branch. + +For every tensor index $I$: + +- If $condition[I] = \text{True}$: + + - $Z[I] = X[I]$ + - $Z_{\textit{err}}^{\textit{propag}}[I] = X_{\textit{err}}[I]$ + +- If $condition[I] = \text{False}$: + + - $Z[I] = Y[I]$ + - $Z_{\textit{err}}^{\textit{propag}}[I] = Y_{\textit{err}}[I]$ + +Therefore: + +$$ +|Z_{\textit{err}}^{\textit{propag}}[I]| = +\begin{cases} +|X_{\textit{err}}[I]| & \text{if } condition[I] = \text{True} \ +|Y_{\textit{err}}[I]| & \text{otherwise} +\end{cases} +$$ + +The operator **Where** does not combine numerical values and does not introduce amplification of propagated errors. +It only forwards the error of the selected input element. + + + +## Error Introduction (real) + +Error introduction for real (ideal) arithmetic is null: + +- $Z_{\textit{err}}^{\textit{intro}} = [0]$. + +The **Where** operator performs no arithmetic computation and therefore introduces no additional numerical error. + + + +## Error Introduction (IEEE-754 floating-point) + +The **Where** operator does not perform any floating-point arithmetic operation. +It only copies either $X[I]$ or $Y[I]$ depending on $condition[I]$. + +Therefore, no rounding is introduced by the operator itself: + +- $Z_{\textit{err}}^{\textit{intro}} = [0]$. + +Any floating-point error present in the output originates solely from the selected input tensor and is entirely captured by $Z_{\textit{err}}^{\textit{propag}}$. + + + +## Error Introduction (int) + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +The **Where** operator performs no arithmetic computation on integer values. +It only selects between two integer operands. + +Therefore: + +- $Z_{\textit{err}}^{\textit{intro}} = [0]$. + +There is no loss of precision and no rounding involved in integer selection. + + + +## Unit Verification + +This section contains a verification scenario to verify the above specification for any C/C++ implementation. +It uses an abstract type `SymbolicDomainError` replacing each real number in the Why3 specification. + +```c++ +Tensor condition; +Tensor X, Y; + +/* condition, X, Y symbolic initialization */ + +auto result = [&condition,&X,&Y](auto I) { + return condition[I] ? X[I] : Y[I]; +}; + +for (auto I : X.indexes()) { + auto z = result(I); + if (condition[I]) { + assert(z.real == X[I].real); + assert(std::abs(z.err) == std::abs(X[I].err)); + } else { + assert(z.real == Y[I].real); + assert(std::abs(z.err) == std::abs(Y[I].err)); + } +} +``` diff --git a/safety-related-profile/sonnx/ops/spec/tests/abs/README.md b/safety-related-profile/sonnx/ops/spec/tests/abs/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/abs/abs_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/abs/abs_doc.ipynb new file mode 100644 index 00000000..3afcd58d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/abs/abs_doc.ipynb @@ -0,0 +1,216 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[ 1.00000000,-2.00000000], [-3.00000000, 4.00000000]]\n", + "Result: abs(X)=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: abs(X)=[[1.00000000], [1.00000000]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: abs(X)=[[0.00000000], [0.00000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: abs(X)=[[1.00000000], [1.00000000]]\n", + "X=[[inf], [inf]]\n", + "Result: abs(X)=[[inf], [inf]]\n", + "X=[[-inf], [-inf]]\n", + "Result: abs(X)=[[inf], [inf]]\n", + "X=[[nan], [nan]]\n", + "Result: abs(X)=[[nan], [nan]]\n", + "X=[[-0.00000000], [-0.00000000]]\n", + "Result: abs(X)=[[0.00000000], [0.00000000]]\n", + "\n", + "## Example 1\n", + "X=[[-2.09999990, 3.40000010,-7.00000000]]\n", + "Result: abs(X)=[[2.09999990,3.40000010,7.00000000]]\n", + "\n", + "## Example 2\n", + "X=[[-1.12300003, 0.00000000], [ 4.00000000,-5.00000000], [ 2.00000000,-3.00000000]]\n", + "Result: abs(X)=[[1.12300003,0.00000000], [4.00000000,5.00000000], [2.00000000,3.00000000]]\n", + "\n", + "## Example 3\n", + "X=[[-2.09999990, -inf, nan,-0.00000000]]\n", + "Result: abs(X)=[[2.09999990, inf, nan,0.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "abs_output_name = \"AbsOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after abs operation)\n", + "abs_output = onnx.helper.make_tensor_value_info(abs_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define Abs node: Y = Abs(X)\n", + "abs_node = onnx.helper.make_node(\"Abs\", [input_name], [abs_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[abs_node],\n", + " name=\"Abs\",\n", + " inputs=[input_tensor],\n", + " outputs=[abs_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Abs is available in opset 13)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) \n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"abs.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"abs.onnx\")\n", + "\n", + "def do_abs(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: abs(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, -2.0],\n", + " [-3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_abs(x)\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, inf, -0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\"), -0.0]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_abs(x_np)\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from ONNX documentation (Abs)\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [-2.1, 3.4, -7]\n", + "x_example_1 = np.array([[-2.1, 3.4, -7.0]], dtype=np.float32)\n", + "do_abs(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[-1.123, 0],\n", + "# [4, -5],\n", + "# [2, -3]]\n", + "x_example_2 = np.array([\n", + " [-1.123, 0.0],\n", + " [4.0, -5.0],\n", + " [2.0, -3.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_abs(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3 (from float spec with nan/inf/-0)\n", + "# X = [-2.1, -Inf, NaN, -0]\n", + "x_example_3 = np.array([\n", + " [-2.1, float(\"-inf\"), float(\"nan\"), -0.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_abs(x_example_3)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/add/README.md b/safety-related-profile/sonnx/ops/spec/tests/add/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/add/hypothesis/test_add.py b/safety-related-profile/sonnx/ops/spec/tests/add/hypothesis/test_add.py new file mode 100644 index 00000000..920f6b1e --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/add/hypothesis/test_add.py @@ -0,0 +1,321 @@ +""" +Using hypothesis to generate automatic tests for Add operator (SONNX) +""" +import math +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +# Equivalence classes for tensor shapes: +# - Singular points and classes for ranks: r_scalar;r_min;]r_min,r_sufficient[;r_sufficient;]r_sufficient,r_max[;r_max. +# Let's set the values: +# - r_scalar = 0 +# - r_min = 1 +# - r_sufficient = 4, which is very popular. So, do we need to consider a rank in ]1,4[? No. +# - r_max = +inf; Not tractable +# - Actual singular points and classes for ranks: r_scalar;r_min;]r_min,r_sufficient[;r_sufficient = 0;1;]1,4[;4 +# - Singular points and classes for dims: no_dim;;dTi_no_element;dTi_min;]dTi_min,dTi_sufficient[;dTi_sufficient;]dTi_sufficient,dTi_max[;dTi_max. +# Let's set the values: +# - no_dim = '-' +# - dTi_no_element = 0 +# - dTi_min = 1 +# - dTi_sufficient = 3 +# - dTi_max = +inf; Not tractable +# - Actual singular points and classes for dims: +# no_dim;;dTi_no_element;dTi_min;]dTi_min,dTi_sufficient[;dTi_sufficient;]dTi_sufficient = -;;0;1;]1,3[;3 +# Combination of ranks and dims, with choice within the open intervals: {0;1;]1,4[;4} X {-;;0;1;]1,3[;3} per dimension for each rank. +# - = {(0,-),(1,0),(1,1),(1,2),(1,3),(2,{0;1;]1,3[;3}X{0;1;]1,3[;3}),(4,{0;1;]1,3[;3}X{0;1;]1,3[;3}X{0;1;]1,3[;3}X{0;1;]1,3[;3})} +# Equivalence classes for A and B elements: +# - EQCELTS: NaN;;-inf;]-inf,0[;0;]0,+inf[;+inf + +# Two families of tests: +# - Tests of the numerical aspects +# - Tests of the dtructural aspects. + +testnumber = 0 + +""" +Function to generate valid inputs for Add operator +""" +@st.composite +def valid_add_args(draw): + global testnumber + print(f"\nTest N1\n") + testnumber=testnumber+1 + print("testnumber = ", testnumber) + # a + info_32 = np.finfo(np.float32) + # La valeur la plus élevée (Positive) + max_32 = info_32.max # Env. 3.40e+38 + # La valeur la plus basse (Négative) + min_32 = info_32.min # Env. -3.40e+38 + # Le plus petit nombre positif au-dessus de zéro (Précision) + tiny_32 = info_32.tiny # Env. 1.17e-38 + x_negative = draw(st.floats(min_value=min_32, max_value=-tiny_32)) + x_positive = draw(st.floats(min_value=tiny_32, max_value=max_32)) + data_a = [[[[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], + [-np.inf,-np.inf,-np.inf,-np.inf,-np.inf,-np.inf], + [x_negative,x_negative,x_negative,x_negative,x_negative,x_negative], + [0,0,0,0,0,0], + [x_positive,x_positive,x_positive,x_positive,x_positive,x_positive], + [np.inf,np.inf,np.inf,np.inf,np.inf,np.inf]]]] + a = np.array(data_a, dtype=np.float32) + # b + data_b = [[[[np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf]]]] + b = np.array(data_b, dtype=np.float32) + + data_ref_y = [[[[np.nan,np.nan,np.nan,np.nan,np.nan,np.nan], + [np.nan,-np.inf,-np.inf,-np.inf,-np.inf,np.nan], + [np.nan,-np.inf,x_negative+x_negative,x_negative,x_negative+x_positive,np.inf], + [np.nan,-np.inf,x_negative,0,x_positive,np.inf], + [np.nan,-np.inf,x_negative+x_positive,x_positive,x_positive+x_positive,np.inf], + [np.nan,np.nan,np.inf,np.inf,np.inf,np.inf]]]] + ref_y = np.array(data_ref_y, dtype=np.float32) + + return a, b, ref_y + +""" +Run ONNX runtime with generated inputs and check constraints +""" +@st.composite +def valid_add_args_S1(draw): +# Structural test S1 +# - selected shape = () = scalar, i.e., rank = 0 and no dimension: (0,-) as equivalence class. +# - EQCELTS: NaN;;-inf;]-inf,0[;0;]0,+inf[;+inf: +# - selected value for a = 31.0 +# - selected value for b = 50.0 + print(f"\nTest S1\n") + a = np.array(31.0, dtype=np.float32) + b = np.array(50.0, dtype=np.float32) + ref_y = np.array(81.0, dtype=np.float32) + + return a, b, ref_y + + +@st.composite +def valid_add_args_S2toS5(draw): +# Structural test S2, S3, S4 and S5, all with rank = 1 +# - selected shapes: +# - S2:(1,0): empty tensors +# - S3:(1,1): 1-element tensors +# - S4:(1,2) +# - S5:(1,3) +# - EQCELTS: NaN;;-inf;]-inf,0[;0;]0,+inf[;+inf: +# - selected values for non-empty a = 31.0 +# - selected values for non-empty b = 50.0 + + range_d0 = 4 + nbtests=range_d0 + tabret = np.empty(nbtests, dtype=object) + + index = 0 + for d0 in range(range_d0): + a = np.full((d0), 31.0, dtype="float32") + b = np.full((d0), 50.0, dtype="float32") + ref_y = np.full((d0), 81.0, dtype="float32") + tabret[index] = [a,b,ref_y] + index += 1 + + return tabret, nbtests + +@st.composite +def valid_add_args_S6toS21(draw): +#(2,{0;1;]1,3[;3}X{0;1;]1,3[;3}) +# Structural test S6, S7, S8...S21, all with rank = 2 +# - selected shapes: {(d0,d1 | d0 and d1 in {0,3})} => 16 cases +# - EQCELTS: NaN;;-inf;]-inf,0[;0;]0,+inf[;+inf: +# - selected values for non-empty a = 31.0 +# - selected values for non-empty b = 50.0 + + range_d0 = 4 + range_d1 = 4 + nbtests = range_d0 * range_d1 + tabret = np.empty(nbtests, dtype=object) + + index = 0 + for d0 in range(range_d0): + for d1 in range(range_d1): + a = np.full((d0, d1), 31.0, dtype="float32") + b = np.full((d0, d1), 50.0, dtype="float32") + ref_y = np.full((d0, d1), 81.0, dtype="float32") + tabret[index] = [a,b,ref_y] + index += 1 + + return tabret, nbtests + + +@st.composite +def valid_add_args_S22toS277(draw): +# (4,{0;1;]1,3[;3}X{0;1;]1,3[;3}X{0;1;]1,3[;3}X{0;1;]1,3[;3}) +# Structural test S6, S7, S8...S85, all with rank = 4 +# - selected shapes: {(d0,d1, d2 | d0, d1 and d2 in {0,3})} => 64 cases +# - EQCELTS: NaN;;-inf;]-inf,0[;0;]0,+inf[;+inf: +# - selected values for non-empty a = 31.0 +# - selected values for non-empty b = 50.0 + + range_d0 = 4 + range_d1 = 4 + range_d2 = 4 + range_d3 = 4 + nbtests = range_d0 * range_d1 * range_d2 * range_d3 + tabret = np.empty(nbtests, dtype=object) + + index = 0 + for d0 in range(range_d0): + for d1 in range(range_d1): + for d2 in range(range_d2): + for d3 in range(range_d3): + a = np.full((d0, d1, d2, d3), 31.0, dtype="float32") + b = np.full((d0, d1, d2, d3), 50.0, dtype="float32") + ref_y = np.full((d0, d1, d2, d3), 81.0, dtype="float32") + tabret[index] = [a,b,ref_y] + index += 1 + + return tabret, nbtests + +@settings(max_examples= 10,deadline=None) +@given(valid_add_args()) +def test_Add(args): + print("--------------------------------------------------") + a, b, ref_y = args + + y, node_def = make_session(a, b, ref_y) + check_test_result_add(y,ref_y) + check_constraints(a, b, y, ref_y, node_def) + +@settings(max_examples= 1,deadline=None) +@given(valid_add_args_S1()) +def test_Add_S1(args): + a, b, ref_y = args + + y, node_def = make_session(a, b, ref_y) + check_test_result_add(y,ref_y) + check_constraints(a, b, y, ref_y, node_def) + +@settings(max_examples= 1,deadline=None) +@given(valid_add_args_S2toS5()) +def test_Add_S2toS5(args): + tabret, nbtests = args + + for nbt in range(nbtests): + indextest=2+nbt + print(f"\nTest S2 to S5:", indextest) + y, node_def = make_session(tabret[nbt][0], tabret[nbt][1], tabret[nbt][2]) + check_test_result_add(y,tabret[nbt][2]) + check_constraints(tabret[nbt][0], tabret[nbt][1], y, tabret[nbt][2], node_def) + + +@settings(max_examples= 1,deadline=None) +@given(valid_add_args_S6toS21()) +def test_Add_S6toS21(args): + tabret, nbtests = args + + for nbt in range(nbtests): + indextest=6+nbt + print(f"\nTest S6 to S21:", indextest) + y, node_def = make_session(tabret[nbt][0], tabret[nbt][1], tabret[nbt][2]) + check_test_result_add(y,tabret[nbt][2]) + check_constraints(tabret[nbt][0], tabret[nbt][1], y, tabret[nbt][2], node_def) + +@settings(max_examples= 1,deadline=None) +@given(valid_add_args_S22toS277()) +def test_Add_S22toS85(args): + tabret, nbtests = args + + for nbt in range(nbtests): + indextest=22+nbt + print(f"\nTest S22 to S277:", indextest) + y, node_def = make_session(tabret[nbt][0], tabret[nbt][1], tabret[nbt][2]) + check_test_result_add(y,tabret[nbt][2]) + check_constraints(tabret[nbt][0], tabret[nbt][1], y, tabret[nbt][2], node_def) + +def make_session(a, b, ref_y): + a_onnx = helper.make_tensor_value_info('a_onnx', helper.np_dtype_to_tensor_dtype(a.dtype), a.shape) + b_onnx = helper.make_tensor_value_info('b_onnx', helper.np_dtype_to_tensor_dtype(b.dtype), b.shape) + + + node_def = helper.make_node( + 'Add', + ['a_onnx', 'b_onnx'], + ['y_onnx'], + ) + + graph_def = helper.make_graph( + [node_def], + 'test-Add', + [a_onnx, b_onnx], + [helper.make_tensor_value_info('y_onnx', TensorProto.FLOAT, ref_y.shape)], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), providers=["CPUExecutionProvider"]) + + y = sess.run(None, {'a_onnx': a, 'b_onnx': b})[0] + + print("a shape:", a.shape) + print("A = ", a) + print("B shape:", b.shape) + print("B = ", b) + + print("Y shape:", y.shape) + print("Y = ", y) + + print("ref_y =", ref_y) + + return y, node_def + + +def check_constraints(a, b, y, refy, node_def): + + #x - Constraints + # C1 + assert a.shape == b.shape + #assert all(dim > 0 for dim in a.shape) + + #Shape of the output + assert y.shape == b.shape + + #Functional check +# assert np.array_equal(y, refy, equal_nan=True) + assert np.allclose(y, refy, rtol=1.2e-01, atol=1.2e-01, equal_nan=True) + +def Add(A, B): + Y = A + B + return Y + +def check_test_result_add(Y,refY): + print("\ncheck_test_result_add\n") + y_f = (np.array2string(Y, separator=',', max_line_width=np.inf).replace('\n', '\n')) + print(f"Y = \n{y_f}") + refy_f = (np.array2string(refY, separator=',', max_line_width=np.inf).replace('\n', '\n')) + print(f"refY = \n{refy_f}") +# if np.array_equal(Y, refY, equal_nan=True): + if np.allclose(Y, refY, rtol=1.2e-01, atol=1.2e-01, equal_nan=True): + print(f"-> Test of add is OK\n") + else: + print(f"-> Test of add is KO\n") \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/batchnormalization/README.md b/safety-related-profile/sonnx/ops/spec/tests/batchnormalization/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/batchnormalization/batch_normalisation_onnx.ipynb b/safety-related-profile/sonnx/ops/spec/tests/batchnormalization/batch_normalisation_onnx.ipynb new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/clip/clip.ipynb b/safety-related-profile/sonnx/ops/spec/tests/clip/clip.ipynb new file mode 100644 index 00000000..44d51dea --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/clip/clip.ipynb @@ -0,0 +1,483 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "02a475ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.3.3)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "input2_name = \"L\"\n", + "input3_name = \"M\"\n", + "clip_output_name = \"Y\"\n", + "\n", + "\n", + "# Create the ONNX model with Clip operator\n", + "def create_clip_model(input_rank, dtype):\n", + "\n", + " #Create \"input-rank\" input tensors and 2 scalars (min and max values for clipping)\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_rank)\n", + " input2 = onnx.helper.make_tensor_value_info(input2_name, dtype, [])\n", + " input3 = onnx.helper.make_tensor_value_info(input3_name, dtype, [])\n", + "\n", + " # Create output tensor (final result after clip operation)\n", + " clip_output = onnx.helper.make_tensor_value_info(clip_output_name, dtype, input_rank)\n", + "\n", + " # Define clip node\n", + " clip_node = onnx.helper.make_node(\n", + " \"Clip\",\n", + " inputs=[input1_name, input2_name, input3_name],\n", + " outputs=[clip_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [clip_node],\n", + " \"Clip\",\n", + " [input1, input2, input3],\n", + " [clip_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"clip.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"clip.onnx\")\n", + " return session\n", + "\n", + "def do_clip(x, l, m, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x, input2_name: l, input3_name: m})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " l_f = (np.array2string(l, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " m_f = (np.array2string(m, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}, L={l_f}, M={m_f}\")\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2,-1, 0, 1, 2], L=-1, M=1\n", + "Result = [-1,-1, 0, 1, 1]\n" + ] + } + ], + "source": [ + "# Case N1: 1-rank tensor (int32), 2 scalars (int32)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([-2, -1, 0, 1, 2], dtype=np.int32)\n", + "l = np.array(-1, dtype=np.int32)\n", + "m = np.array(1, dtype=np.int32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "638b9c9d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[-20,-10, 0, 10, 20], [-15,-10, 5, 10, 0]], L=-1, M=1\n", + "Result = [[-1,-1, 0, 1, 1], [-1,-1, 1, 1, 0]]\n" + ] + } + ], + "source": [ + "# Case N2: 2-rank tensor (int32), 2 scalars (int32)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[-20,-10,0,10,20],[-15,-10,5,10,0]], dtype=np.int32)\n", + "l = np.array(-1, dtype=np.int32)\n", + "m = np.array(1, dtype=np.int32)\n", + "session = create_clip_model([None, None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8a4e6329", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], L=-1.00000000, M=1.00000000\n", + "Result = [-1.00000000,-1.00000000, 0.00000000, 1.00000000, 1.00000000]\n" + ] + } + ], + "source": [ + "# Case N3: 1-rank tensor (float32), 2 scalars (float32)\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0], dtype=np.float32)\n", + "l = np.array(-1.0, dtype=np.float32)\n", + "m = np.array(1.0, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "78867c4a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], [-5.00000000,-2.50000000, 0.00000000, 2.50000000, 5.00000000]], L=-3.00000000, M=3.00000000\n", + "Result = [[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], [-3.00000000,-2.50000000, 0.00000000, 2.50000000, 3.00000000]]\n" + ] + } + ], + "source": [ + "# Case N4: 2-rank tensor (float32), 2 scalars (float32)\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([[-2.0, -1.0, 0.0, 1.0, 2.0],[-5.0, -2.5, 0.0, 2.5, 5.0]], dtype=np.float32)\n", + "l = np.array(-3.0, dtype=np.float32)\n", + "m = np.array(3.0, dtype=np.float32)\n", + "session = create_clip_model([None,None], onnx_type)\n", + "do_clip(x, l, m, session)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "223f48c0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], [-5.00000000,-2.50000000, 0.00000000, 2.50000000, 5.00000000]], L=10.00000000, M=5.00000000\n", + "Result = [[5.00000000,5.00000000,5.00000000,5.00000000,5.00000000], [5.00000000,5.00000000,5.00000000,5.00000000,5.00000000]]\n" + ] + } + ], + "source": [ + "# Case N5: 2-rank tensor (float32), 2 scalars (float32), min > max\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([[-2.0, -1.0, 0.0, 1.0, 2.0],[-5.0, -2.5, 0.0, 2.5, 5.0]], dtype=np.float32)\n", + "l = np.array(10.0, dtype=np.float32)\n", + "m = np.array(5.0, dtype=np.float32)\n", + "session = create_clip_model([None,None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "markdown", + "id": "1c8a25b9", + "metadata": {}, + "source": [ + "## No nominal cases (nan and inf values)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "8a0e757d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2.00000000,-1.00000000, 0.00000000, 1.00000000, nan], L=-1.00000000, M=1.00000000\n", + "Result = [-1.00000000,-1.00000000, 0.00000000, 1.00000000, nan]\n" + ] + } + ], + "source": [ + "# Case N1: 1-rank tensor (float32), 2 scalars (float32) - Nan values in input tensor\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-2.0, -1.0, 0.0, 1.0, np.nan], dtype=np.float32)\n", + "l = np.array(-1.0, dtype=np.float32)\n", + "m = np.array(1.0, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "e663f77e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], L=nan, M=1.00000000\n", + "Result = [-2.00000000,-1.00000000, 0.00000000, 1.00000000, 1.00000000]\n" + ] + } + ], + "source": [ + "# Case N2: 1-rank tensor (float32), 2 scalars (float32) - Nan values in L\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0], dtype=np.float32)\n", + "l = np.array(np.nan, dtype=np.float32)\n", + "m = np.array(1.0, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "1ca723dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], L=-1.00000000, M=nan\n", + "Result = [-1.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000]\n" + ] + } + ], + "source": [ + "# Case N3: 1-rank tensor (float32), 2 scalars (float32) - Nan values in M\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0], dtype=np.float32)\n", + "l = np.array(-1, dtype=np.float32)\n", + "m = np.array(np.nan, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "e8bb2b3d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000], L=nan, M=nan\n", + "Result = [-2.00000000,-1.00000000, 0.00000000, 1.00000000, 2.00000000]\n" + ] + } + ], + "source": [ + "# Case N4: 1-rank tensor (float32), 2 scalars (float32) - Nan values in L and M\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-2.0, -1.0, 0.0, 1.0, 2.0], dtype=np.float32)\n", + "l = np.array(np.nan, dtype=np.float32)\n", + "m = np.array(np.nan, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3dfed7ff", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[ -inf,-1.00000000, 0.00000000, 1.00000000, inf], L=-1.00000000, M=nan\n", + "Result = [-1.00000000,-1.00000000, 0.00000000, 1.00000000, inf]\n" + ] + } + ], + "source": [ + "# Case N5: 1-rank tensor (float32), 2 scalars (float32) - Inf values in input tensor and Nan value in M\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([-np.inf, -1.0, 0.0, 1.0, np.inf], dtype=np.float32)\n", + "l = np.array(-1, dtype=np.float32)\n", + "m = np.array(np.nan, dtype=np.float32)\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x, l, m, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a0252258", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Case Z1: X contains +/-0.0, L=+0.0, M=+0.0 ---\n", + "X=[ 0.00000000,-0.00000000, 0.00000000,-0.00000000], L=0.00000000, M=0.00000000\n", + "Result = [ 0.00000000,-0.00000000, 0.00000000,-0.00000000]\n", + "\n", + "--- Case Z2: X around zero, L=-0.0, M=+0.0 ---\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=-0.00000000, M=0.00000000\n", + "Result = [-0.00000000,-0.00000000, 0.00000000, 0.00000000]\n", + "\n", + "--- Case Z3: X around zero, L=+0.0, M=-0.0 (L > M logic) ---\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=0.00000000, M=-0.00000000\n", + "Result = [ 0.00000000,-0.00000000, 0.00000000,-0.00000000]\n", + "\n", + "--- Case Z4: Matrix of combinations ---\n", + "\n", + "Testing with L=0.0 (sign=False), M=0.0 (sign=False)\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=0.00000000, M=0.00000000\n", + "Result = [ 0.00000000,-0.00000000, 0.00000000, 0.00000000]\n", + "\n", + "Testing with L=-0.0 (sign=True), M=-0.0 (sign=True)\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=-0.00000000, M=-0.00000000\n", + "Result = [-0.00000000,-0.00000000, 0.00000000,-0.00000000]\n", + "\n", + "Testing with L=-0.0 (sign=True), M=0.0 (sign=False)\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=-0.00000000, M=0.00000000\n", + "Result = [-0.00000000,-0.00000000, 0.00000000, 0.00000000]\n", + "\n", + "Testing with L=0.0 (sign=False), M=-0.0 (sign=True)\n", + "X=[-0.50000000,-0.00000000, 0.00000000, 0.50000000], L=0.00000000, M=-0.00000000\n", + "Result = [ 0.00000000,-0.00000000, 0.00000000,-0.00000000]\n" + ] + } + ], + "source": [ + "## Signed Zero Cases (+0.0 vs -0.0)\n", + "# Testing all combinations of input, L, and M with signed zeros.\n", + "\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "\n", + "# 1. Combinaisons d'entrées X avec L et M fixés à 0\n", + "x_zeros = np.array([0.0, -0.0, 0.0, -0.0], dtype=np.float32)\n", + "l_pos = np.array(0.0, dtype=np.float32)\n", + "m_pos = np.array(0.0, dtype=np.float32)\n", + "\n", + "print(\"--- Case Z1: X contains +/-0.0, L=+0.0, M=+0.0 ---\")\n", + "session = create_clip_model([None], onnx_type)\n", + "do_clip(x_zeros, l_pos, m_pos, session)\n", + "\n", + "# 2. Combinaisons avec L négatif (-0.0) et M positif (+0.0)\n", + "l_neg = np.array(-0.0, dtype=np.float32)\n", + "m_pos = np.array(0.0, dtype=np.float32)\n", + "x_test = np.array([-0.5, -0.0, 0.0, 0.5], dtype=np.float32)\n", + "\n", + "print(\"\\n--- Case Z2: X around zero, L=-0.0, M=+0.0 ---\")\n", + "do_clip(x_test, l_neg, m_pos, session)\n", + "\n", + "# 3. Combinaisons avec L positif (+0.0) et M négatif (-0.0) -> Cas L > M\n", + "# Note: Ici L (0.0) est \"strictement\" supérieur à M (-0.0) selon certains tris binaires, \n", + "# mais égal selon IEEE 754. ONNX Runtime suit généralement la règle L > M => Result = M.\n", + "l_pos = np.array(0.0, dtype=np.float32)\n", + "m_neg = np.array(-0.0, dtype=np.float32)\n", + "\n", + "print(\"\\n--- Case Z3: X around zero, L=+0.0, M=-0.0 (L > M logic) ---\")\n", + "do_clip(x_test, l_pos, m_neg, session)\n", + "\n", + "# 4. Toutes les combinaisons de L et M sur des entrées variées\n", + "print(\"\\n--- Case Z4: Matrix of combinations ---\")\n", + "combinations = [\n", + " (0.0, 0.0), # L=+0, M=+0\n", + " (-0.0, -0.0), # L=-0, M=-0\n", + " (-0.0, 0.0), # L=-0, M=+0\n", + " (0.0, -0.0) # L=+0, M=-0\n", + "]\n", + "\n", + "for l_val, m_val in combinations:\n", + " l_proto = np.array(l_val, dtype=np.float32)\n", + " m_proto = np.array(m_val, dtype=np.float32)\n", + " print(f\"\\nTesting with L={l_val} (sign={np.signbit(l_val)}), M={m_val} (sign={np.signbit(m_val)})\")\n", + " do_clip(x_test, l_proto, m_proto, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/README.md b/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/README.md new file mode 100644 index 00000000..76203e92 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/README.md @@ -0,0 +1,35 @@ +# How to run +There are two main scripts, test_clip.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_clip.py you need to do +```bash +pytest test_clip.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_clip.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_clip.py + +# Explanations of Clip corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Clip tests +For the clip operator we just **checked both the corner and edge cases** for the **shape_size_input_tensor_x**, **size_input_axis**, **l_tensor** and **m_tensor**. + +We consider **shape_size_input_tensor_x**, **size_input_axis**, **l_tensor** and **m_tensor** as **lines** since they are independent and we checked the corner and edge cases for both of them. For **l_tensor** and **m_tensor** we have multiple lines since we have multiple types of data. + +We also check if there is at least one test where **l_tensor** is greater than **m_tensor** to check if the function can handle inverted boundaries. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/check_edges.py new file mode 100644 index 00000000..9d565e92 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/clip/hypothesis/check_edges.py @@ -0,0 +1,145 @@ +""" +This file checks edge cases in the generated data for the Clip operation in ONNX. +""" +import json +import numpy as np +import ml_dtypes + +clip_types = { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BFLOAT16": ml_dtypes.bfloat16 +} + + +def check_edges(data): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + + check_shape_size_input_x = check_individual_variables( + "shape_size_input_tensors_x", data["shape_size_input_x"], + data["min_shape_size_input_x"], data["max_shape_size_input_x"]) + + size_input_axis = data["size_input_axis"] + size_input_axis_flatten = [item for sublist in size_input_axis for item in sublist] + check_size_input_axis = check_individual_variables( + "size_input_axis", size_input_axis_flatten, + data["min_size_input_axis"], data["max_size_input_axis"]) + + check_l_tensor = check_bondaries_tensors("l_tensor", data["l_tensor"]) + check_m_tensor = check_bondaries_tensors("m_tensor", data["m_tensor"]) + + check_inverted_boundaries_ = check_inverted_boundaries("l_tensor", data["l_tensor"], "m_tensor", data["m_tensor"]) + + return all([check_shape_size_input_x, check_size_input_axis, + check_l_tensor, check_m_tensor, check_inverted_boundaries_]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + +def check_bondaries_tensors(variable_name, variable_analysis): + """ + Check interval tensors + """ + minimum_dtypes = get_minimum_dtype() + maximum_dtypes = get_maximum_dtype() + separated_analysis = separate_by_dtype(variable_analysis) + checks = [] + for key, value in separated_analysis.items(): + checks.append(check_individual_variables( + f"{variable_name} - {key}", value, minimum_dtypes[key], maximum_dtypes[key])) + return all(checks) + + +def separate_by_dtype(variable_analysis): + """ + Separate the analysis by dtype + """ + separated_analysis = {np.dtype(dtype).name: [] for dtype in clip_types.values()} + for item in variable_analysis: + dtype = str(item[1]) + if dtype.startswith(" X [C2] + l = draw(hnp.arrays(dtype=dtype, shape=[], elements=a_numeric)) + + #--------------------------------------------------- + # Input M + #--------------------------------------------------- + + # Create input tensor M + # M [C1] -> X [C2] + m = draw(hnp.arrays(dtype=dtype, shape=[], elements=a_numeric)) + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + + # Y [C1] -> X [C1] + y_shape = x_shape + + return (x_shape, x, l, m, dtype_name, y_shape) + +""" +Function that runs the test +""" +@settings(max_examples=20000, deadline=None) +@given(valid_clip_args()) +def test_clip(args): + x_shape, x, l, m, dtype_name, y_shape = args + + generated_data["shape_size_input_x"].append(len(x_shape)) + generated_data["size_input_axis"].append(x_shape) + generated_data["l_tensor"].append(l) + generated_data["m_tensor"].append(m) + + y = run_onnx_clip(x_shape, x, l, m, dtype_name, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + check_constraints(x, l, m, y) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated for testing ONNX Clip Operator", + "min_shape_size_input_x": inputs_attributes["min_shape_size_input_x"], + "max_shape_size_input_x": inputs_attributes["max_shape_size_input_x"], + "shape_size_input_x": generated_data["shape_size_input_x"], + "min_size_input_axis": inputs_attributes["min_size_input_axis"], + "max_size_input_axis": inputs_attributes["max_size_input_axis"], + "size_input_axis": generated_data["size_input_axis"], + "l_tensor": generated_data["l_tensor"], + "m_tensor": generated_data["m_tensor"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + # Process data before writing to file + data["l_tensor"] = [(arr.tolist(), str(arr.dtype)) for arr in generated_data["l_tensor"]] + data["m_tensor"] = [(arr.tolist(), str(arr.dtype)) for arr in generated_data["m_tensor"]] + json.dump(data, f, indent=4) + + +def run_onnx_clip(x_shape, x, l, m, dtype_name, y_shape, provider): + """ + Function that runs the ONNX Clip operation + """ + + # Create inputs + x_onnx = helper.make_tensor_value_info('x', + helper.np_dtype_to_tensor_dtype(x.dtype), + list(x_shape)) + l_onnx = helper.make_tensor_value_info('l', + helper.np_dtype_to_tensor_dtype(l.dtype), + []) + m_onnx = helper.make_tensor_value_info('m', + helper.np_dtype_to_tensor_dtype(m.dtype), + []) + + node_def = helper.make_node( + 'Clip', + ['x', 'l', 'm'], + ['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-clip', + [x_onnx, l_onnx, m_onnx], + [helper.make_tensor_value_info('y', + helper.np_dtype_to_tensor_dtype(x.dtype), + y_shape)], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 13 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'x': x, 'l': l, 'm': m})[0] + + for tensor in [x, l, m]: + print("x shape:", tensor.shape) + print("x values:", tensor) + + print("y shape:", y.shape) + print("y values:", y) + print("y data type:", y.dtype) + return y + +def check_constraints(x, l, m, y): + """ + Function that defines asserts for the constraints + """ + + # Inputs Constraints + # X [C1] - Shape consistency + # Y [C1] -> X [C1] + assert x.shape == y.shape + + #X [C2] - Type consistency + # L [C1] -> X [C2] + # M [C1] -> X [C2] + # Y [C3] -> X [C2] + assert x.dtype == y.dtype == l.dtype == m.dtype + + # Output Constraints + # Y [C2] + if l <= m: + for value in np.nditer(y): + assert l <= value <= m + else: + np.all(y == m) diff --git a/safety-related-profile/sonnx/ops/spec/tests/common/README.md b/safety-related-profile/sonnx/ops/spec/tests/common/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/README.md b/safety-related-profile/sonnx/ops/spec/tests/concat/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/README.md b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/README.md new file mode 100644 index 00000000..a8ffa654 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/README.md @@ -0,0 +1,39 @@ +# How to run +There are two main scripts, test_concat.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_concat.py you need to do +```bash +pytest test_concat.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_concat.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_concat.py + +# Explanations of Concat corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Concat tests +For the concat operator we just **checked both the corner and edge cases** for the **number_of_input_tensors** and the **shape_size_input**. + +We consider both (**number of input tensors and shape of input tensors**) as **lines** since they are independent and we checked the corner and edge cases for both of them. + +We **didn't check** the corner and edges cases for the **concatenation_axis** and for **input_tensors_shapes** because the way we generate them is not independent (actualy **concatenation_axis** depends on the number of axis generated while **input_tensor_shapes** depends on the number of input tensors). + +For example, imagine that my number of input tensors is 1, if i try to check the corner cases and the edge cases for this example it will fail, because i will just have one tensor. It would be impossible to check the corner and edges cases in this situation. + +The same happens for the **concatenation_axis**. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/check_edges.py new file mode 100644 index 00000000..79ac9018 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/check_edges.py @@ -0,0 +1,48 @@ +""" +This file checks edge cases in the generated data for the Concat operation in ONNX. +""" + +import json + +def check_edges(data): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + check_number_of_input_tensors = check_individual_variables( + "number_of_input_tensors", data["number_of_input_tensors"], + data["min_input_tensors"], data["max_input_tensors"]) + + check_shape_size_input_tensors = check_individual_variables( + "shape_size_input_tensors", data["shape_size_input"], + data["min_shape_size_input"], data["max_shape_size_input"]) + + # TODO check concatenation_axis and input_tensors_shapes is not done (Doubts) + return all([check_number_of_input_tensors, check_shape_size_input_tensors]) + + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + +print(check_edges("generated_data.json")) diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/test_concat.py b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/test_concat.py new file mode 100644 index 00000000..42c14c66 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/concat/hypothesis/test_concat.py @@ -0,0 +1,321 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Concat operation in ONNX. +""" +import os + +import json +import numpy as np +import ml_dtypes + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + +from onnx import helper +import onnx.reference + +import tensorflow as tf + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes details +""" +inputs_attributes = { + "min_input_tensors": 1, # Input [C1] + "max_input_tensors": 10, # Change as needed. Max is 2^31 -1 + "min_shape_size_input": 1, # Input [C3] + "max_shape_size_input": 10, # Change as needed. Max is 2^31 -1 + "min_concatenation_axis": 0, # Axis [C1] + "min_dim_for_concatenation_axis": 0, # Change as needed + "max_dim_for_concatenation_axis": 10, # Change as needed + "min_dim_for_other_axes": 0, #Change as needed + "max_dim_for_other_axes": 10 # Change as needed +} + +""" +Concat supported types +""" +# TODO we try to convert, but ONNXruntime does not run. ONNXruntime supports bfloat16? +# ONNX concat reference implementation supports bfloat16. + +concat_types = { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool, + "BFLOAT16": ml_dtypes.bfloat16 +} + +""" +Store generated data +""" +generated_data = { + "number_of_input_tensors" : [], + "shape_size_input" : [], + "concatenation_axis" : [], + "max_concatenation_axis" : [], + "input_tensors_shapes" : [] +} + +""" +Function to generate valid concatenation arguments +""" +@st.composite +def valid_concat_args(draw): + # Input [C1] + number_of_input_tensors = draw(st.integers( + min_value=inputs_attributes["min_input_tensors"], + max_value=inputs_attributes["max_input_tensors"])) + + # Input [C3] + shape_size_input_tensors = draw(st.integers( + min_value=inputs_attributes["min_shape_size_input"], + max_value=inputs_attributes["max_shape_size_input"])) + max_concatenation_axis = shape_size_input_tensors - 1 + generated_data["max_concatenation_axis"].append(max_concatenation_axis) + + # Axis [C1] + concatenation_axis = draw(st.integers( + min_value=inputs_attributes["min_concatenation_axis"], + max_value=max_concatenation_axis)) + + # Input [C2] + dim_for_other_axis = {} + for i in range(shape_size_input_tensors): + if i != concatenation_axis: + dim_for_other_axis[i] = draw(st.integers( + min_value=inputs_attributes["min_dim_for_other_axes"], + max_value=inputs_attributes["max_dim_for_other_axes"])) + + # Generate shapes for input tensors + input_tensors_shapes = [] + for _ in range(number_of_input_tensors): + shape = [] + for j in range(shape_size_input_tensors): + if j != concatenation_axis: + dim_size = dim_for_other_axis[j] + else: + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_for_concatenation_axis"], + max_value=inputs_attributes["max_dim_for_concatenation_axis"])) + shape.append(dim_size) + input_tensors_shapes.append(shape) + + # Generate input tensors + dtype_name = draw(st.sampled_from(list(concat_types.keys()))) + dtype = concat_types[dtype_name] + + if np.issubdtype(dtype, np.integer): + min_val = np.iinfo(dtype).min + max_val = np.iinfo(dtype).max + elements_strategy = st.integers(min_value=min_val, max_value=max_val) + elif np.issubdtype(dtype, np.floating): + min_val = np.finfo(dtype).min + max_val = np.finfo(dtype).max + elements_strategy = st.floats(min_value=min_val, max_value=max_val) + elif np.issubdtype(dtype, np.bool_): + elements_strategy = st.booleans() + elif np.issubdtype(dtype, np.str_): + elements_strategy = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + elif dtype_name == "BFLOAT16": + min_bfloat16 = float(ml_dtypes.finfo(concat_types["BFLOAT16"]).min) + max_bfloat16 = float(ml_dtypes.finfo(concat_types["BFLOAT16"]).max) + elements_strategy = st.floats(min_value=min_bfloat16, max_value=max_bfloat16) + + + tensors = [] + for shape in input_tensors_shapes: + if dtype_name == "BFLOAT16": + temp_tensor = draw(hnp.arrays(dtype=np.float32, shape=shape, elements=elements_strategy)) + tf_tensor = tf.cast(tf.constant(temp_tensor), tf.bfloat16) + tensor = tf_tensor.numpy() + tensors.append(tensor) + else: + tensor = draw(hnp.arrays(dtype=dtype, shape=shape, elements=elements_strategy)) + tensors.append(tensor) + + + y_concatenation_axis = 0 + for shape in input_tensors_shapes: + y_concatenation_axis += shape[concatenation_axis] + + y_shape = [] + + # Output [C1] + for i in range(shape_size_input_tensors): + if i != concatenation_axis: + y_shape.append(input_tensors_shapes[0][i]) + else: + y_shape.append(y_concatenation_axis) + + return input_tensors_shapes,tensors, concatenation_axis, y_shape, number_of_input_tensors, shape_size_input_tensors, dtype_name + +""" +Function that runs the test +""" +@settings(max_examples=1000, deadline=None) +@given(valid_concat_args()) +def test_concat(args): + input_tensors_shapes, tensors, concatenation_axis, y_shape, number_of_input_tensors, shape_size_input_tensors, dtype_name = args + + generated_data["number_of_input_tensors"].append(number_of_input_tensors) + generated_data["shape_size_input"].append(shape_size_input_tensors) + generated_data["concatenation_axis"].append(concatenation_axis) + generated_data["input_tensors_shapes"].append(input_tensors_shapes) + + run_onnx_concat(input_tensors_shapes, tensors, concatenation_axis, y_shape, dtype_name) + check_constraints(len(tensors), input_tensors_shapes, + concatenation_axis, y_shape) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + dados = { + "titulo": "Dados gerados pelo Hypothesis", + "min_input_tensors": inputs_attributes["min_input_tensors"], + "max_input_tensors": inputs_attributes["max_input_tensors"], + "number_of_input_tensors": generated_data["number_of_input_tensors"], + "min_shape_size_input": inputs_attributes["min_shape_size_input"], + "max_shape_size_input": inputs_attributes["max_shape_size_input"], + "shape_size_input": generated_data["shape_size_input"], + "min_concatenation_axis": inputs_attributes["min_concatenation_axis"], + "max_concatenation_axis": generated_data["max_concatenation_axis"], + "min_dim_for_concatenation_axis": inputs_attributes["min_dim_for_concatenation_axis"], + "max_dim_for_concatenation_axis": inputs_attributes["max_dim_for_concatenation_axis"], + "concatenation_axis": generated_data["concatenation_axis"], + "min_dim_for_other_axes": inputs_attributes["min_dim_for_other_axes"], + "max_dim_for_other_axes": inputs_attributes["max_dim_for_other_axes"], + "input_tensors_shapes": generated_data["input_tensors_shapes"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(dados, f, indent=4) + + +def run_onnx_concat(input_tensors_shapes, tensors, concatenation_axis, y_shape, dtype_name): + """ + Function that runs the ONNX Concat operation + """ + input_tensor_infos = [] + input_names = [] + + # Create inputs + for i, shape in enumerate(input_tensors_shapes): + input_name = f'input_{i}' + input_names.append(input_name) + + tensor_info = helper.make_tensor_value_info( + input_name, + helper.np_dtype_to_tensor_dtype(tensors[i].dtype), + shape + ) + input_tensor_infos.append(tensor_info) + + # Create a node (Concat) with inputs/outputs + node_def = helper.make_node( + 'Concat', + input_names, + ['y'], + axis=concatenation_axis + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-concat', + input_tensor_infos, + [helper.make_tensor_value_info('y', + helper.np_dtype_to_tensor_dtype(tensors[0].dtype), + y_shape)], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 15 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + if dtype_name == "BFLOAT16": + # Usar ONNX Reference Implementation + sess = onnx.reference.ReferenceEvaluator(onnx_model) + else: + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + input_dic = {} + for i, tensor in enumerate(tensors): + input_name = f'input_{i}' + input_dic[input_name] = tensor + + + y = sess.run(None, input_dic)[0] + + for x in tensors: + print("x shape:", x.shape) + print("x values:", x) + + print("Concatenation axis:", concatenation_axis) + print("y shape:", y.shape) + print("y values:", y) + print("y data type:", y.dtype) + + +def check_constraints(number_of_input_tensors, + input_tensors_shapes, concatenation_axis, y_shape): + + """ + Function that defines asserts for the constraints + """ + + # Inputs Constraints + # [C1] - Input tensors range + assert ( number_of_input_tensors >= inputs_attributes["min_input_tensors"] + and number_of_input_tensors <= inputs_attributes["max_input_tensors"]) + + # [C2] - Shape Consistency + assert all(x[j] == input_tensors_shapes[0][j] for x in input_tensors_shapes + for j in range(len(x)) if j != concatenation_axis) + + # [C3] - Shape size range + assert all(len(i) >= inputs_attributes["min_shape_size_input"] + and len(i) <= inputs_attributes["max_shape_size_input"] for i in input_tensors_shapes) + + # Attributes Constraints + # [C1] - Concatenation axis range + assert ( concatenation_axis >= inputs_attributes["min_concatenation_axis"] + and concatenation_axis <= len(input_tensors_shapes[0]) - 1) + + # Outputs Constraints + # [C1] - Output concatenation axis dimension + assert ( y_shape[concatenation_axis] == sum(input_tensors_shapes[i][concatenation_axis] + for i in range(len(input_tensors_shapes)))) + # [C1] - Other axis dimension + assert ( y_shape[x] == input_tensors_shapes[0][x] for x in range(len(y_shape)) + if x != concatenation_axis) diff --git a/safety-related-profile/sonnx/ops/spec/tests/concat/onnx_concat.py b/safety-related-profile/sonnx/ops/spec/tests/concat/onnx_concat.py new file mode 100644 index 00000000..2f7486b4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/concat/onnx_concat.py @@ -0,0 +1,1021 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# ---------------------------------------------------------------------------- +# Created By : Salome Marty Laurent +# Created Date: 08/04/2025 +# version ='3.0' +# --------------------------------------------------------------------------- + +""" This file was designed to caracterize and test the ONNX operator: Concat + +Five main tests are defined: + - Concat with two empty tensors as scalar values (28) + - Concat with one input tensor (line 100) + - Concat with vector inputs (line 166) + - Concat with input matrixes (line 236) + - Concat with input matrixes of different types (line 324) + - Concat with 3D tensors inputs (line 423) + - Concat with 4D tensors inputs (line 514) + - Concat with 4D tensors in a reverse order (line 587) + - Concat with string matrixes (line 660 ) + - Concat with boolean matrixes (line 744 ) + - Concat with complex64 matrixes (line 823 ) + - Concat with complex128 matrixes (line 920 )""" + + +# --------------------------------------------------------------------------- + +import onnxruntime +import onnx +import numpy + +print(onnxruntime.__version__) + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test two empty tensors as scalar values """ + +# --------------------------------------------------------------------------- + +x_val_info = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, []) +y_val_info = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, []) + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', + ['x', 'y'], + ['c'], + axis=0, +) + + +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat-scalars', + [x_val_info, y_val_info], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [2])], +) + +onnx_model = onnx.helper.make_model(graph_def, producer_name='onnx-scalar-concat-test') + +# Set Opset +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +try: + onnx.checker.check_model(onnx_model) +except onnx.checker.ValidationError as e: + print(f"ONNX model invalid: {e}") + + +print("\nTest with scalar inputs for Concat (axis=0):\n") +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Initialize tensors +x_np = numpy.array(1.0, dtype=numpy.float32) +y_np = numpy.array(2.0, dtype=numpy.float32) + + +# Do inference with try-except block +try: + print("\nAttempting to create InferenceSession and run...") + sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + c_result = sess.run(None, {'x': x_np, 'y': y_np})[0] + + print("\nInference successful (if this line is reached).") + print("Output C shape:", c_result.shape) + print("Output C value:", c_result) + + +except onnxruntime.capi.onnxruntime_pybind11_state.Fail as e: + print("\n--- ONNX Runtime Execution Error Caught ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + print("This error is expected if ONNX Runtime's Concat shape inferencer not validates axis for rank-0 inputs.") + +except Exception as e: + print("\n--- An Unexpected Error Occurred ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + +print("\nScript execution continued after inference attempt.") + + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with one input tensor """ + +# --------------------------------------------------------------------------- + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [1]) + + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', # node name + ['x'], # inputs + ['c'], # output + axis=0, # Axis 0 +) + +# Create the graph +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat', + [x], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [1])], +) + +onnx_model = onnx.helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) +print("\n Test with scalar inputs: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = 1* numpy.ones((1), dtype=numpy.float32) + + + +c = sess.run(None, {'x': x})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("C shape:", c.shape) +print("C:", c) + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with vector inputs """ + +# --------------------------------------------------------------------------- + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [1]) +y = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [1]) +z = onnx.helper.make_tensor_value_info('z', onnx.helper.TensorProto.FLOAT, [1]) + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y', 'z'], # inputs + ['c'], # output + axis=0, # Axis 0 +) + +# Create the graph +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat', + [x, y, z], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [3])], +) + +onnx_model = onnx.helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) +print("\n Test with scalar inputs: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = 1* numpy.ones((1), dtype=numpy.float32) +y = 2* numpy.ones((1), dtype=numpy.float32) +z = 3* numpy.ones((1), dtype=numpy.float32) + + +c = sess.run(None, {'x': x, 'y':y, 'z': z})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("Y shape:", y.shape) +print("Y:", y) + +print("Z shape:", + z.shape) +print("Z:", z) + +print("C shape:", c.shape) +print("C:", c) + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with input matrixes """ + +# --------------------------------------------------------------------------- + + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [2, 3]) +y = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [4, 3]) +z = onnx.helper.make_tensor_value_info('z', onnx.helper.TensorProto.FLOAT, [3, 3]) + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y', 'z'], # inputs + ['c'], # output + axis=0, # Axis 0 +) + +# Create the graph +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat', + [x, y, z], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [9, 3])], +) + +onnx_model = onnx.helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +print("\n Test with input matrixes: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + +x = numpy.array([[1, 2, 3], + [4, 5, 6]], dtype=numpy.float32) + + +y = numpy.array([[7, 8, 9], + [10, 11, 12], + [13, 14, 15], + [16, 17, 18]], dtype=numpy.float32) + + +z = numpy.array([[20, 21, 22], + [23, 24, 25], + [26, 27, 28]], dtype=numpy.float32) + + +c = sess.run(None, {'x': x, 'y':y, 'z': z})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("Y shape:", y.shape) +print("Y:", y) + +print("Z shape:", + z.shape) +print("Z:", z) + +print("C shape:", c.shape) +print("C:", c) + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with different types input matrixes """ + +# --------------------------------------------------------------------------- + + +# Create inputs +x_type = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.INT8, [2, 3]) +y_type = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [4, 3]) +z_type = onnx.helper.make_tensor_value_info('z', onnx.helper.TensorProto.INT8, [3, 3]) + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y', 'z'], # inputs + ['c'], # output + axis=0, # Axis 0 +) + +# Create the graph +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat', + [x_type, y_type, z_type], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [9, 3])], +) + +onnx_model = onnx.helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +print("\n Test with input matrixes: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + + + +x_type = numpy.array([[1, 2, 3], + [4, 5, 6]], dtype=numpy.int8) + + +y_type = numpy.array([[7, 8, 9], + [10, 11, 12], + [13, 14, 15], + [16, 17, 18]], dtype=numpy.float32) + + +z_type = numpy.array([[20, 21, 22], + [23, 24, 25], + [26, 27, 28]], dtype=numpy.int8) + + +# Do Inference with try-except block +try: + print("X shape:", x_type.shape) + print("X:", x_type) + + print("Y shape:", y_type.shape) + print("Y:", y_type) + + print("Z shape:", z_type.shape) + print("Z:", z_type) + print("\nAttempting to create InferenceSession and run...") + sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + c_result = sess.run(None, {'x': x_type, 'y': y_type, 'z': z_type})[0] + + print("\nInference successful.") + print("Output C shape:", c_result.shape) + print("Output C value:\n", c_result) + +except onnxruntime.capi.onnxruntime_pybind11_state.Fail as e: + print("\n--- ONNX Runtime Execution Error Caught ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + +except Exception as e: + print("\n--- An Unexpected Error Occurred During Inference ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + import traceback + traceback.print_exc() + + + + + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with 3D inputs tensors """ + +# --------------------------------------------------------------------------- + + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [2, 3, 4]) +y = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [2, 3, 4]) + + +# Create a node (Concat) with input/outputs +node_def_4D = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y'], # inputs + ['c'], # output + axis=2, # Axis 2 +) + + +# Create the graph +graph_def_4D = onnx.helper.make_graph( + [node_def_4D], + 'test-concat', + [x, y], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [2, 3, 8])], +) + +onnx_model = onnx.helper.make_model(graph_def_4D) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +print("\n Test with 3D input tensors: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = numpy.array([ + [ + [ 1.00, 2.00, 3.00, 10.00 ], + [ 4.00, 5.00, 6.00, 11.00 ], + [ 7.00, 8.00, 9.00, 12.00 ] + ], + [ + [ 11.00, 12.00, 13.00, 20.00 ], + [ 14.00, 15.00, 16.00, 21.00 ], + [ 17.00, 18.00, 19.00, 22.00 ] + ] +], dtype=numpy.float32) + + +y = numpy.array([ + [ + [ 101.00, 102.00, 103.00, 110.00 ], + [ 104.00, 105.00, 106.00, 120.00 ], + [ 107.00, 108.00, 109.00, 130.00 ] + ], + [ + [ 111.00, 112.00, 113.00, 120.00 ], + [ 114.00, 115.00, 116.00, 121.00 ], + [ 117.00, 118.00, 119.00, 122.00 ] + ] +], dtype=numpy.float32) + + +c = sess.run(None, {'x': x, 'y':y})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("Y shape:", y.shape) +print("Y:", y) + + +print("C shape:", c.shape) +print("C:", c) + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with 4D tensors inputs """ + +# --------------------------------------------------------------------------- + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [1, 1, 3, 2]) +y = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [1, 3, 3, 2]) +z = onnx.helper.make_tensor_value_info('z', onnx.helper.TensorProto.FLOAT, [1, 2, 3, 2]) +w = onnx.helper.make_tensor_value_info('w', onnx.helper.TensorProto.FLOAT, [1, 4, 3, 2]) + + +# Create a node (Concat) with input/outputs +node_def = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y', 'z', 'w'], # inputs + ['c'], # output + axis=1, # Axis 1 +) + +# Create the graph +graph_def = onnx.helper.make_graph( + [node_def], + 'test-concat', + [x, y, z, w], + [onnx.helper.make_tensor_value_info('c', onnx.helper.TensorProto.FLOAT, [1, 10, 3, 2])], +) + +onnx_model = onnx.helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +print("\n Test with 4D tensor inputs: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = 3* numpy.ones((1, 1, 3, 2), dtype=numpy.float32) +y = 4* numpy.ones((1, 3, 3, 2), dtype=numpy.float32) +z = 5* numpy.ones((1, 2, 3, 2), dtype=numpy.float32) +w = 6* numpy.ones((1, 4, 3, 2), dtype=numpy.float32) + +c = sess.run(None, {'x': x, 'y':y, 'z': z, 'w': w})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("Y shape:", y.shape) +print("Y:", y) + +print("Z shape:", + z.shape) +print("Z:", z) + +print("W shape:", w.shape) +print("W:", w) + +print("C shape:", c.shape) +print("C:", c) + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with 4D tensors inputs in a reverse order """ + +# --------------------------------------------------------------------------- + +# Create inputs +x = onnx.helper.make_tensor_value_info('x', onnx.helper.TensorProto.FLOAT, [1, 1, 3, 2]) +y = onnx.helper.make_tensor_value_info('y', onnx.helper.TensorProto.FLOAT, [1, 3, 3, 2]) +z = onnx.helper.make_tensor_value_info('z', onnx.helper.TensorProto.FLOAT, [1, 2, 3, 2]) +w = onnx.helper.make_tensor_value_info('w', onnx.helper.TensorProto.FLOAT, [1, 4, 3, 2]) + +# Create a node (Concat) with input/outputs +node_def_reverse = onnx.helper.make_node( + 'Concat', # node name + ['w', 'z', 'y', 'x'], # inputs + ['c_reverse'], # output + axis=1, # Axis 1 +) + +# Create the graph +graph_def_reverse = onnx.helper.make_graph( + [node_def_reverse], + 'test-concat-reverse', + [w, z, y, x], + [onnx.helper.make_tensor_value_info('c_reverse', onnx.helper.TensorProto.FLOAT, [1, 10, 3, 2])], +) + +onnx_model_reverse = onnx.helper.make_model(graph_def_reverse) + +# Let's freeze the opset. +del onnx_model_reverse.opset_import[:] +opset = onnx_model_reverse.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model_reverse.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model_reverse) + +print("\n Test with 4D tensor inputs in a reverse order: \n") +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model_reverse.graph)) + +# Do inference +sess = onnxruntime.InferenceSession(onnx_model_reverse.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = 3* numpy.ones((1, 1, 3, 2), dtype=numpy.float32) +y = 4* numpy.ones((1, 3, 3, 2), dtype=numpy.float32) +z = 5* numpy.ones((1, 2, 3, 2), dtype=numpy.float32) +w = 6* numpy.ones((1, 4, 3, 2), dtype=numpy.float32) + +c_reverse = sess.run(None, {'w': w, 'z':z, 'y': y, 'x': x})[0] + +print("W shape:", w.shape) +print("W:", w) + +print("Z shape:", z.shape) +print("Z:", y) + +print("Y shape:", + y.shape) +print("Y:", z) + +print("X shape:", x.shape) +print("X:", x) + +print("C shape:", c_reverse.shape) +print("C:", c_reverse) + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with string input matrixes """ + +# --------------------------------------------------------------------------- + + +x_val_info = onnx.helper.make_tensor_value_info('x', onnx.TensorProto.STRING, [2, 3]) +y_val_info = onnx.helper.make_tensor_value_info('y', onnx.TensorProto.STRING, [2, 3]) + +# Output 'c' will be a [4,3] string tensor when concatenating along axis=0 +output_shape = [4, 3] +c_val_info = onnx.helper.make_tensor_value_info('c', onnx.TensorProto.STRING, output_shape) + +# Create the Concat Node +node_def_string = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y'], # inputs + ['c'], # output + axis=0, # Concatenate along the first axis (rows) +) + +# 3. Create the Graph +graph_def_string = onnx.helper.make_graph( + [node_def_string], + 'test-concat-strings', + [x_val_info, y_val_info], + [c_val_info], +) + +# 4. Create the Model +onnx_model = onnx.helper.make_model(graph_def_string, producer_name='onnx-string-concat-test') + +# 5. Set Opset +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + + +onnx.checker.check_model(onnx_model) + + + +print("\nTest with 2D string input tensors for Concat (axis=0):\n") +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Initialize NumPy Tensors with Python strings + +x_np = numpy.array([ + ['Alpha', 'Beta', 'Gamma'], + ['Delta', 'Epsilon', 'Zeta'] +], dtype=object) + +y_np = numpy.array([ + ['Eta', 'Theta', 'Iota'], + ['Kappa', 'Lambda', 'Mu'] +], dtype=object) + +print("\nInput X shape:", x_np.shape, "\nX value:\n", x_np) +print("\nInput Y shape:", y_np.shape, "\nY value:\n", y_np) + +#Do Inference with try-except block + +print("\nAttempting to create InferenceSession and run...") +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + +c_result = sess.run(None, {'x': x_np, 'y': y_np})[0] + +print("\nInference successful.") +print("Output C shape:", c_result.shape) +print("Output C value:\n", c_result) + +# Verify the result +expected_c = numpy.array([ + ['Alpha', 'Beta', 'Gamma'], + ['Delta', 'Epsilon', 'Zeta'], + ['Eta', 'Theta', 'Iota'], + ['Kappa', 'Lambda', 'Mu'] +], dtype=object) + + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with boolean input matrixes """ + +# --------------------------------------------------------------------------- + +x_val_info = onnx.helper.make_tensor_value_info('x', onnx.TensorProto.BOOL, [2, 3]) +y_val_info = onnx.helper.make_tensor_value_info('y', onnx.TensorProto.BOOL, [2, 3]) + + +concat_axis = 1 +output_shape = [2, 6] +c_val_info = onnx.helper.make_tensor_value_info('c', onnx.TensorProto.BOOL, output_shape) + +# Create the Concat Node +node_def_bool = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y'], # inputs + ['c'], # output + axis=concat_axis, +) + +# Create the Graph +graph_def_bool = onnx.helper.make_graph( + [node_def_bool], + 'test-concat-booleans', + [x_val_info, y_val_info], + [c_val_info], +) + +print("\n") +print("\nTest with 2D boolean input tensors for Concat (axis=1):\n") +# Create the Model +onnx_model = onnx.helper.make_model(graph_def_bool, producer_name='onnx-boolean-concat-test') + +# Set Opset +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 # Concat for booleans is well-supported +onnx_model.ir_version = 8 + +# Verify the Model + +onnx.checker.check_model(onnx_model) + + + +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Initialize NumPy Tensors with boolean values +x_np = numpy.array([ + [True, False, True], + [False, True, False] +], dtype=bool) + +y_np = numpy.array([ + [False, False, True], + [True, True, False] +], dtype=bool) + +print("\nInput X shape:", x_np.shape, "\nX value:\n", x_np) +print("\nInput Y shape:", y_np.shape, "\nY value:\n", y_np) + + +sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +c_result = sess.run(None, {'x': x_np, 'y': y_np})[0] + +print("\nInference successful.") +print("Output C shape:", c_result.shape) +print("Output C value:\n", c_result) + +expected_c = numpy.array([ + [True, False, True, False, False, True], + [False, True, False, True, True, False] +], dtype=bool) + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with complex64 input matrixes """ + +# --------------------------------------------------------------------------- + +x_val_info = onnx.helper.make_tensor_value_info('x', onnx.TensorProto.COMPLEX64, [2, 2]) +y_val_info = onnx.helper.make_tensor_value_info('y', onnx.TensorProto.COMPLEX64, [3, 2]) + + +concat_axis = 0 +output_shape = [5, 2] +c_val_info = onnx.helper.make_tensor_value_info('c', onnx.TensorProto.COMPLEX64, output_shape) + +# Create the Concat Node +node_def_complex = onnx.helper.make_node( + 'Concat', + ['x', 'y'], + ['c'], + axis=concat_axis, # Concatenate along the specified axis +) + +# Create the Graph +graph_def_complex = onnx.helper.make_graph( + [node_def_complex], + 'test-concat-complex', + [x_val_info, y_val_info], + [c_val_info], +) + +# Create the Model + +onnx_model = onnx.helper.make_model(graph_def_complex, producer_name='onnx-complex-concat-test') + +# Set Opset +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 # Concat for complex types is supported +onnx_model.ir_version = 8 + + +onnx.checker.check_model(onnx_model) + +print(f"\nTest with 2D complex input tensors for Concat (axis={concat_axis}):\n") +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Initialize NumPy Tensors with complex values + +x_np = numpy.array([ + [1+2j, 3+4j], + [5+6j, 7+8j] +], dtype=numpy.complex64) + +y_np = numpy.array([ + [9+10j, 11+12j], + [13+14j, 15+16j], + [17+18j, 19+20j] +], dtype=numpy.complex64) + +print("\nInput X shape:", x_np.shape, "\nX value:\n", x_np) +print("\nInput Y shape:", y_np.shape, "\nY value:\n", y_np) + +# Do Inference + +try: + print("\nAttempting to create InferenceSession and run...") + sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + c_result = sess.run(None, {'x': x_np, 'y': y_np})[0] + + print("\nInference successful.") + print("Output C shape:", c_result.shape) + print("Output C value:\n", c_result) + + # Verify the result + expected_c = numpy.array([ + [1+2j, 3+4j], + [5+6j, 7+8j], + [9+10j, 11+12j], + [13+14j, 15+16j], + [17+18j, 19+20j] + ], dtype=numpy.complex64) + +except onnxruntime.capi.onnxruntime_pybind11_state.Fail as e: + print("\n--- ONNX Runtime Execution Error Caught ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + +except Exception as e: + print("\n--- An Unexpected Error Occurred During Inference ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + import traceback + traceback.print_exc() + +# --------------------------------------------------------------------------- + +""" ONNX Concat operator: test with complex128 input matrixes """ + +# --------------------------------------------------------------------------- + +x_val_info = onnx.helper.make_tensor_value_info('x', onnx.TensorProto.COMPLEX128, [2, 2]) +y_val_info = onnx.helper.make_tensor_value_info('y', onnx.TensorProto.COMPLEX128, [3, 2]) + + +concat_axis = 0 +output_shape = [5, 2] +c_val_info = onnx.helper.make_tensor_value_info('c', onnx.TensorProto.COMPLEX128, output_shape) + +# Create the Concat Node +node_def_complex = onnx.helper.make_node( + 'Concat', # node name + ['x', 'y'], # inputs + ['c'], # output + axis=concat_axis, +) + +# Create the Graph +graph_def_complex = onnx.helper.make_graph( + [node_def_complex], + 'test-concat-complex128', + [x_val_info, y_val_info], + [c_val_info], +) + +# 4. Create the Model +onnx_model = onnx.helper.make_model(graph_def_complex, producer_name='onnx-complex128-concat-test') + +# 5. Set Opset +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 # Concat for complex types is supported +onnx_model.ir_version = 8 + +# 6. Verify the Model +try: + onnx.checker.check_model(onnx_model) + print("ONNX model with complex128 inputs is valid.") +except onnx.checker.ValidationError as e: + print(f"ONNX model invalid: {e}") + exit(1) # Exit if model check fails + +print(f"\nTest with 2D complex128 input tensors for Concat (axis={concat_axis}):\n") +print(onnx.helper.printable_graph(onnx_model.graph)) + +# 7. Initialize NumPy Tensors with complex values +# dtype=numpy.complex128 corresponds to onnx.TensorProto.COMPLEX128 +x_np = numpy.array([ + [1+2j, 3+4j], + [5+6j, 7+8j] +], dtype=numpy.complex128) + +y_np = numpy.array([ + [9+10j, 11+12j], + [13+14j, 15+16j], + [17+18j, 19+20j] +], dtype=numpy.complex128) + +print("\nInput X shape:", x_np.shape, "\nX value:\n", x_np) +print("\nInput Y shape:", y_np.shape, "\nY value:\n", y_np) + +# Do Inference with try-except block +try: + print("\nAttempting to create InferenceSession and run...") + sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + c_result = sess.run(None, {'x': x_np, 'y': y_np})[0] + + print("\nInference successful.") + print("Output C shape:", c_result.shape) + print("Output C value:\n", c_result) + + # Verify the result + expected_c = numpy.array([ + [1+2j, 3+4j], + [5+6j, 7+8j], + [9+10j, 11+12j], + [13+14j, 15+16j], + [17+18j, 19+20j] + ], dtype=numpy.complex128) + + +except onnxruntime.capi.onnxruntime_pybind11_state.Fail as e: + print("\n--- ONNX Runtime Execution Error Caught ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + +except Exception as e: + print("\n--- An Unexpected Error Occurred During Inference ---") + print(f"Error Type: {type(e)}") + print(f"Error Message: {e}") + import traceback + traceback.print_exc() + + +print("\nScript execution finished.") + diff --git a/safety-related-profile/sonnx/ops/spec/tests/constant/README.md b/safety-related-profile/sonnx/ops/spec/tests/constant/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/README.md b/safety-related-profile/sonnx/ops/spec/tests/conv/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/documents/conv_specification_example/tests/conv_onnx.ipynb b/safety-related-profile/sonnx/ops/spec/tests/conv/conv_onnx.ipynb similarity index 100% rename from safety-related-profile/documents/conv_specification_example/tests/conv_onnx.ipynb rename to safety-related-profile/sonnx/ops/spec/tests/conv/conv_onnx.ipynb diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/bias-testing.py b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/bias-testing.py new file mode 100644 index 00000000..9d59c622 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/bias-testing.py @@ -0,0 +1,71 @@ +# @title Standard convolution (3 channels) +import numpy +from onnx import * +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + +# Create inputs +x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [1, 1, 2, 2]) +w = helper.make_tensor_value_info('w', TensorProto.FLOAT, [2, 1, 1, 1]) +b = helper.make_tensor_value_info('b', TensorProto.FLOAT, [2]) + +# Create a node (Conv) with input/outputs +node_def = helper.make_node( + 'Conv', # node name + ['x', 'w', 'b'], # inputs + ['y'], # outputs + dilations=[1,1], + kernel_shape=[1,1], + pads=[0, 0, 0, 0], + strides=[1, 1], + auto_pad='NOTSET', + group=1, # Standard convolution +) + +# Create the graph +graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x, w, b], + [helper.make_tensor_value_info('y', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = numpy.ones((1, 1, 2, 2), dtype=numpy.float32) +w = numpy.ones((2, 1, 1, 1), dtype=numpy.float32) +b = numpy.array([5.0,5.0], dtype=numpy.float32) + +y = sess.run(None, {'x': x, 'w':w, 'b': b})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("W shape:", w.shape) +print("W:", w) + +print("B shape:", + b.shape) +print("B:", b) + +print("Y shape:", y.shape) +print("Y:", y) diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/convReview.py b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/convReview.py new file mode 100644 index 00000000..5a04d128 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/convReview.py @@ -0,0 +1,303 @@ +import math +import numpy +import onnx.checker +from onnx import helper, TensorProto +from onnxruntime import InferenceSession + +# * Adding Padding to the Input Matrix +def padTrasform(X,pads): + ## pads = [left,top,right,bottom] + m_Lines = len(X) + n_Cols = len(X[0]) + top = [[0 for j in range(0, n_Cols + pads[0] + pads[2])] for i in range(0, pads[1])] + bottom = [[0 for j in range(0, n_Cols + pads[0] + pads[2])] for i in range(0, pads[3])] + padded_rows = [] + for x in X: + new_row = [0]*pads[0] + list(x) + [0]*pads[2] + padded_rows.append(new_row) + final = top + padded_rows + bottom + return final + +# * Adding Dilation to the Kernel Matrix +def kernelDilation(W, dilation): + dW2 = len(W) + dW3 = len(W[0]) + x_dil = dilation[0] + y_dil = dilation[1] + + horiz_W = [] + for row in W: + new_row = [] + for j in range(dW3): + new_row.append(row[j]) + if j < dW3 - 1: + for _ in range(y_dil - 1): + new_row.append(0) + horiz_W.append(new_row) + + new_W = [] + for i in range(len(horiz_W)): + new_W.append(horiz_W[i]) + if i < len(horiz_W) - 1: + for _ in range(x_dil - 1): + new_W.append([0] * len(horiz_W[0])) + return new_W + + +""" Suggetion for Dy2 and DY3 Calculation """ +# The dilated kernel is equal to the original kernel multiplied by the dilation factor +# and subtracted by ( dilation factor minus 1 ) because no zeros are added at the last margin. +# However, the value that was already in the matrix remains +# Assuming that dilation[0] refers to rows and dilation[1] to columns + +""" +dw2 - original kernel rows +dilation[0] - dilation factor for rows + +dW2_p = dW2 * dilation[0] - (dilation[0] - 1) + +dw3 - original kernel columns +dilation[1] - dilation factor for columns + +dW3_p = dW3 * dilation[1] - (dilation[1] - 1) + +""" + +def calculateDy2(dX2,pads,dW2,strides,dilation): + alpha = dX2 + pads[1] + pads[3] + theta = dilation[0] * (dW2 - 1) + 1 + finalResult = (alpha - theta) // strides[1] + 1 + return finalResult + +def calculateDy3(dX3,pads,dW3,strides,dilation): + beta = dX3 + pads[0] + pads[2] + gamma = dilation[1] * (dW3 - 1) + 1 + finalResult = (beta - gamma) // strides[0] + 1 + return finalResult + +## *! Wrong Dy2 Calculation +def wrongDy2(dX2,pads,dW2,strides,dilation): + alpha = dX2 + pads[0] + pads[2] + theta = dilation[0] * dW2 - 1 + finalResult = math.floor((alpha - theta) / strides[0]) + 1 + return finalResult + +## *! Wrong Dy3 Calculation +def wrongDy3(dX3,pads,dW3,strides,dilation): + beta = dX3 + pads[1] + pads[3] + gamma = dilation[1] * dW3 - 1 + finalResult = math.floor((beta - gamma) / strides[1]) + 1 + return finalResult + +def standConvDef(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + dW2 = len(W) + dW3 = len(W[0]) + + dX2 = len(X) + dX3 = len(X[0]) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + print("X_p:") + printTensor(X_p) + print("W_p:") + printTensor(W_p) + + dY2 = calculateDy2(dX2,pads,dW2,strides,dilation) + dY3 = calculateDy3(dX3,pads,dW3,strides,dilation) + + print("dY2:",dY2) + print("dY3:",dY3) + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Important Change + Y[m][n] += X_p[m*strides[1]+j][n*strides[0]+z] * W_p[j][z] + + return Y + +# *! Wrong Version of their code (Sum Order) +def standConvDefWrong(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + print("X_p:") + printTensor(X_p) + print("W_p:") + printTensor(W_p) + + dY2 = 4 + dY3 = 4 + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Wrong Change + Y[m][n] += X_p[m*strides[0]+j][n*strides[1]+z] * W_p[j][z] + + return Y + +# *! Wrong Version of their code (Calculation of dY2 and dY3) +def standConvDefWrongY(X,W): + ## Move kernel 2 steps right and 1 step down + strides = (2,3) + pads = [1,2,2,2] + dilation = (2,2) + + X_p = padTrasform(X,pads) + W_p = kernelDilation(W,dilation) + + dY2 = wrongDy2(len(X),pads,len(W),strides,dilation) + dY3 = wrongDy3(len(X[0]),pads,len(W[0]),strides,dilation) + + print("dY2:",dY2) + print("dY3:",dY3) + + dW2_p = len(W_p) + dW3_p = len(W_p[0]) + + Y = [[0 for j in range(0, dY3)] for i in range(0, dY2)] + + for m in range(dY2): + for n in range(dY3): + for j in range(dW2_p): + for z in range(dW3_p): + ## TODO: Important Change + Y[m][n] += X_p[m*strides[1]+j][n*strides[0]+z] * W_p[j][z] + + return Y + + +X = [ + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1], + [1,1,1,1,1,1,1,1] +] + +W = [ + [1,1,1], + [1,1,1] +] + + +def printTensor(T): + for x in T: + print(x) + +""" +Correct Version +""" +Y = standConvDef(X,W) +print("\nFinal Matrix Y (Correct):") +printTensor(Y) + +""" +Wrong Version of their code (Sum Order) +""" +#Y_wrong = standConvDefWrong(X,W) +#print("\nFinal Matrix Y (Wrong):") +#printTensor(Y_wrong) + +""" +Wrong Version of their code (Calculation of dY2 and dY3) +""" +#Y_wrong = standConvDefWrongY(X,W) +#print("\nFinal Matrix Y (Wrong):") +#printTensor(Y_wrong) + +print("\n") +print("ONNX CHECK") + +"""" +ONNX CHECK +""" +x_Onnx = helper.make_tensor_value_info('x_Onnx', TensorProto.FLOAT, [1, 1, 8, 8]) +w_Onnx = helper.make_tensor_value_info('w_Onnx', TensorProto.FLOAT, [1, 1, 3, 2]) +b_Onnx = helper.make_tensor_value_info('b_Onnx', TensorProto.FLOAT, [1, 1]) + +node_def = helper.make_node( + 'Conv', + ['x_Onnx', 'w_Onnx', 'b_Onnx'], + ['y_Onnx'], + dilations=[2,2], + kernel_shape=[3,2], + pads=[1, 2, 2, 2], + strides=[2, 3], + auto_pad='NOTSET', + group=1, +) + +graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_Onnx, w_Onnx, b_Onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = numpy.ones((1, 1, 8, 8), dtype=numpy.float32) +w = numpy.ones((1, 1, 3, 2), dtype=numpy.float32) +b = numpy.ones((1, 1), dtype=numpy.float32) + +y = sess.run(None, {'x_Onnx': x, 'w_Onnx':w, 'b_Onnx': b})[0] + +print("X shape:", x.shape) +print("X:", x) + +print("W shape:", w.shape) +print("W:", w) + +print("B shape:", + b.shape) +print("B:", b) + +print("Y shape:", y.shape) +print("Y:", y) diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesis-conv.py b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesis-conv.py new file mode 100644 index 00000000..2e4285c9 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesis-conv.py @@ -0,0 +1,243 @@ +""" +Using hypothesis to generate automatic tests for conv operator (SONNX) +""" +import math +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +AUTO_PAD_OPTIONS = ["NOTSET", "VALID", "SAME_UPPER", "SAME_LOWER"] + +""" +Function to generate valid inputs/atributes for Conv operator +""" +@st.composite +def valid_conv_args(draw): + # x and w dimensions + dx0 = draw(st.integers(min_value=1, max_value=10)) + dx1 = draw(st.integers(min_value=1, max_value=10)) + dx2 = draw(st.integers(min_value=1, max_value=100)) + dx3 = draw(st.integers(min_value=1, max_value=100)) + dw0 = draw(st.integers(min_value=1, max_value=10)) + dw1 = dx1 + dw2 = draw(st.integers(min_value=1, max_value=dx2)) + dw3 = draw(st.integers(min_value=1, max_value=dx3)) + # x and w tensors + x = draw(hnp.arrays(dtype=np.float32, shape=(dx0, dx1, dx2, dx3))) + w = draw(hnp.arrays(dtype=np.float32, shape=(dw0, dw1, dw2, dw3))) + #FIXME: Should BIAS be W1 or W0? + bias = draw(hnp.arrays(dtype=np.float32, shape=(dw1,))) + + # Atributes + pads = draw(st.lists( + st.integers(min_value=0, max_value=1000), min_size=4, max_size=4) + )#FIXME: Check this Max Value + strides = draw(st.lists( + st.integers(min_value=1, max_value=1000), min_size=2, max_size=2) + ) #FIXME: Check this Max Value + auto_pad = draw(st.sampled_from(AUTO_PAD_OPTIONS)) + kernel_shape = [dw2, dw3] + + # Auxiliary variables + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + line_dilation_max = math.floor((myalpha-1) /(dw2 -1)) if dw2 > 1 else 1 + column_dilation_max = math.floor((mybeta-1) /(dw3 -1)) if dw3 > 1 else 1 + line_dilation_value = draw(st.integers(min_value=1, max_value=line_dilation_max)) + column_dilation_value = draw(st.integers(min_value=1, max_value=column_dilation_max)) + + # Atributes + dilation = [line_dilation_value, column_dilation_value] + + return x, w, bias, pads, strides, dilation, auto_pad, kernel_shape + +""" +Run ONNX runtime with generated inputs/atributes and check constraints +""" +@settings(max_examples= 1000,deadline=None) +@given(valid_conv_args()) +def test_conv(args): + print("--------------------------------------------------") + x, w, bias, pads, strides, dilation, auto_pad, kernel_shape = args + dx0, dx1, dx2, dx3 = x.shape + dw0, dw1, dw2, dw3 = w.shape + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw2 - 1)) + 1 + mygamma = (dilation[1] * (dw3 - 1)) + 1 + mydy2 = math.floor((myalpha - (mytheta)) / strides[0]) + 1 + mydy3 = math.floor((mybeta - (mygamma)) / strides[1]) + 1 + + dy2 = math.floor( (myalpha - (dilation[0] * dw2 - 1)) / strides[0] ) + 1 + dy3 = math.floor( (mybeta - (dilation[1] * dw3 - 1)) / strides[1] ) + 1 + + #FIXME: Review bias dimension + x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, dx2, dx3]) + w_onnx = helper.make_tensor_value_info('w_onnx', TensorProto.FLOAT, [dw0, dw1, dw2, dw3]) + b_onnx = helper.make_tensor_value_info('b_onnx', TensorProto.FLOAT, [dw1]) + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilation, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + group=1, + ) + else: + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + kernel_shape=[dw2, dw3], + strides=strides, + auto_pad=auto_pad, + group=1, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_onnx, w_onnx, b_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dw0, mydy2, mydy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + # Initialize tensors + x = x.reshape(dx0, dx1, dx2, dx3).astype(np.float32) + w = w.reshape(dw0, dw1, dw2, dw3).astype(np.float32) + b = bias.reshape((dw1,)).astype(np.float32) + + print("Pads", pads) + print("Strides", strides) + print("Dilation", dilation) + print("Auto_pad", auto_pad) + y = sess.run(None, {'x_onnx': x, 'w_onnx':w, 'b_onnx': b})[0] + + print("x shape:", x.shape) + #print("x:", x) + + print("w shape:", w.shape) + #print("w:", w) + + print("B shape:",b.shape) + #print("B:", b) + + print("dy2:", dy2) + print("dy3:", dy3) + print("mydy2:", mydy2) + print("mydy3:", mydy3) + print("Y shape:", y.shape) + #print("Y:", y) + + check_constraints(x, w, auto_pad, y, mydy2, + mydy3, kernel_shape, b, strides, + node_def, pads, dilation) + + +def check_constraints(x, w, auto_pad, y, mydy2, + mydy3, kernel_shape, b, strides, + node_def, pads, dilation): + + #x - Constraints + # C1 + assert x.ndim == 4 and x.shape[2] >= 0 and x.shape[3] >= 0 + # C2 + assert x.shape[1] == w.shape[1] + # C3 #FIXME: REVIEW THIS BECAUSE OF AUTO_PAD + if auto_pad == "NOTSET": + assert y.shape[2] == mydy2 and y.shape[3] == mydy3 + # C4 ?? + assert x.ndim == 4 + assert all(dim > 0 for dim in x.shape) + + + #w - Constraints + # C1 + # Same of C2 of x + # C2 + #Same of C3 of x + # C3 + kernel_shape[0] == w.shape[2] and kernel_shape[1] == w.shape[3] + # C4 + assert w.ndim == 4 + assert all(dim > 0 for dim in w.shape) + + + #B - Constraints + #C1 + assert b.shape[0] == w.shape[1] + + #Strides - Constraints + # C1 + assert all(s > 0 for s in strides) + # C2 + assert len(strides) == 2 + # And same of C3 of x + + + #Auto_pad - Constraints + #C1 + assert auto_pad in AUTO_PAD_OPTIONS + #C2 + if auto_pad != "NOTSET": + assert all(attr.name!="pads" for attr in node_def.attribute) + + + #Pads - Constraints + # C1 + assert all(p >= 0 for p in pads) + # C2 + assert len(pads) == (x.ndim - 2) * 2 + # C3 + # Same of C3 of x + + + #Dilation - Constraints + # C1 + assert all(d > 0 for d in dilation) + # C2 + assert len(dilation) == (w.ndim - 2) + # C3 + # Same of C3 of x + + + #kernel shape - Constraints + # C1 + for kernel_value in kernel_shape: + assert kernel_value > 0 + # C2 + #Same of C3 of w + + #Y - Constraints + # C1 + #Same of C3 of x + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesisReview_Joao_Ricardo.md b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesisReview_Joao_Ricardo.md new file mode 100644 index 00000000..1d59e929 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/hypothesisReview_Joao_Ricardo.md @@ -0,0 +1,175 @@ +# Hypothesis Review + +After some tests with hypothesis for the Conv operator we arrived to some conclusions. + +Regarding the comparison between ONNX Runtime and the ONNX reference implementation (https://github.com/onnx/onnx/blob/main/onnx/reference/ops_optimized/op_conv_optimized.py), we identified the following inconsistencies: +- Limits on pad values +- Discrepancy in output values when AutoPad = NOTSET +- Discrepancy in output shapes when AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +- Discrepancy in output values when AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) + +## 1 - Limits on Pad + +This inconsistency was detected using **Hypothesis** to generate all inputs and attributes (except for Auto_pad, which was fixed to NOTSET), while respecting all constraints on attributes and inputs as defined in the informal specification. + +The ONNX reference implementation is unable to handle cases where the padding in a specific region (top, left, bottom, right) exceeds the corresponding spatial dimension of the kernel. In other words, this occurs whenever the kernel overlaps with X_padded during convolution but does **not intersect any original values of X**. +The following image illustrates an example of this scenario. + +![Example](imgs/imagem.jpg) + +In this context, two possible scenarios were identified: + +### 1.1 - Shape Errors +Although the ONNX reference implementation fails to run, ON**NX Runtime executes successfully and returns the expected result**. +In this specific case, we used **PyTorch** as a validation tool to confirm the correctness of ONNX Runtime's output. + +``` +Example: +x = np.ones((1, 1, 2, 1)).astype(np.float32) +w = np.zeros((1, 1, 2, 1)).astype(np.float32) +b = np.zeros(1).astype(np.float32) +auto_pad = "NOTSET" +dilations = [1, 1] +group = 1 +kernel_shape = [2, 1] +pads = [0, 0, 3, 0] +strides = [1, 1] +``` + +![Example](imgs/imagem1.jpg) + + +![Example](imgs/imagem3.jpg) + + +![Example](imgs/imagem4.jpg) + + +### 1.2 - Erroneous execution due to line and column Replication +Even more concerning than the previous case is the fact that there are specific inputs for which the **ONNX reference implementation does not raise an error**, but instead **computes the convolution incorrectly**. + +We believe the error occurs because **Python allows access to list indices using negative numbers**. + +![Example](imgs/imagem5.jpg) + +- ih1 should be the index where the window begins +- ih2 should be the index where the window ends + +To determine whether this was indeed a problem with the reference implementation, we also validated the ONNX Runtime's execution using **PyTorch**. + +The following image illustrates the tested example. + +![Example](imgs/imagem6.jpg) + +``` +Example: +x = np.arange(16).reshape(1, 1, 4, 4).astype(np.float32) +w = np.ones((1, 1, 3, 2), dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +dilations = [1,1] +strides = [1,1] +pads= [4,0,0,0] +auto_pad = "NOTSET" +group = 1 +kernel_shape = [3,2] +``` + +![Example](imgs/imagem7.jpg) + +(The penultimate row is correct; however, the first row should have been entirely filled with zeros. This was not the case, as it was computed in the same way as the penultimate row.) + +![Example](imgs/imagem8.jpg) + +![Example](imgs/imagem9.jpg) + + +## 2 – Discrepancy in Output Values +This inconsistency was detected using **Hypothesis** to generate all inputs and attributes (except for Auto_pad, which was fixed to NOTSET), while respecting all constraints from the informal spec. + +In this context, the output tensors computed by **ONNX Runtime** and the **ONNX Reference Implementation** diverge for certain inputs. +We believe this may be due to **rounding differences** or **operations being performed in different orders**. + +``` +Example +x = np.full((1, 1, 4, 1), 17, dtype=np.float32) +w = np.array([ + [[[-999788.], [0.], [999785.]]], + [[[999785.], [999785.], [999785.]]]], dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +dilations = [1,1] +strides = [1,1] +pads= [0,0,0,0] +auto_pad = "NOTSET" +group = 1 +kernel_shape = [3,1] +``` + +![Example](imgs/imagem10.jpg) + +## 3 – Discrepancy in Output Shapes for AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +This inconsistency was detected using **Hypothesis**, varying Auto_pad between SAME_UPPER and SAME_LOWER, while respecting all constraints from the informal spec. + +The output dimensions from **ONNX Runtime** follow the formula: + +$$output\_shape[i] = \left\lceil \frac{input\_shape[i]}{strides[i]} \right\rceil$$ + +(see [Conv - ONNX 1.20.0 documentation](https://onnx.ai/onnx/operators/onnx__Conv.html#l-onnx-doc-conv), Auto_Pad section) +ONNX Reference Implementation did not compute this correctly, resulting in **shape errors** and **empty tensors** in some cases. + +We believe the issue stems from incorrect indexing in the reference code using indices 0 and 1 (batch size and input channels) instead of 2 and 3 (spatial dimensions). + +``` +Example +x = np.arange(10).reshape(2, 1, 1, 5).astype(np.float32) +w = np.ones((1, 1, 3, 3), dtype=np.float32) +b = np.zeros((1, 1), dtype=np.float32) +auto_pad = "SAME_LOWER" +dilations = [1,1] +strides = [2,2] +group = 1 +kernel_shape = [3,3] +With the fixes we propose no shape inconsistencies were detected. +``` + +![Example](imgs/imagem11.jpg) + +![Example](imgs/imagem12.jpg) + +## 4 – Discrepancy in Output Values for AutoPad ≠ NOTSET (SAME_UPPER and SAME_LOWER) +This inconsistency was also detected using **Hypothesis**, varying Auto_pad between SAME_UPPER and SAME_LOWER, while respecting all constraints from the informal spec. + +This particular error occurs on the **ONNX Runtime side**: when the input dimension value exceeds the stride for a given axis, the convolution is not computed correctly. + + +``` +Example +x = np.array([[[[0, 1, 1, 1, 1]]]]).astype(np.float32) +w = np.ones((1, 1, 1, 1), dtype=np.float32) +auto_pad = "SAME_LOWER" +dilations = [1,1] +strides = [1,5] +group = 1 +kernel_shape = [1,1] +``` + +![Example](imgs/imagem13.jpg) + +![Example](imgs/imagem14.jpg) + + +## Questions +- It’s now clear that ONNX Runtime and ONNX Reference Implementation diverge. + +- To what extent should one be used over the other? + +- Can one be reliably used to validate the other? + +- When Auto_pad is different from NOTSET, it seems to introduce significant source of randomness. + +- We have no control over how Auto_Pad is calculated in the Runtime, and given the inconsistencies we found, it’s not safe to assume it matches the reference. + +- We were not able to found any documentation saying how the Valid option for Auto_Pad works. The only available states that: "VALID mean no padding" (ONNX version 1 for Conv Operator). However, in the reference implementation Valid is treated in the same way as SAME_UPPER. + +- Are additional constraints needed beyond those in the informal spec? + +- Given what is said above how should we continue? \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem.jpg new file mode 100644 index 00000000..42a7b3c8 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem1.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem1.jpg new file mode 100644 index 00000000..41c8dc55 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem1.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem10.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem10.jpg new file mode 100644 index 00000000..41440f05 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem10.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem11.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem11.jpg new file mode 100644 index 00000000..4a885a12 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem11.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem12.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem12.jpg new file mode 100644 index 00000000..bacb68a0 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem12.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem13.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem13.jpg new file mode 100644 index 00000000..2f4ac2b6 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem13.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem14.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem14.jpg new file mode 100644 index 00000000..304f5f4a Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem14.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem3.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem3.jpg new file mode 100644 index 00000000..fc6422a1 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem3.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem4.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem4.jpg new file mode 100644 index 00000000..ed49df31 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem4.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem5.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem5.jpg new file mode 100644 index 00000000..d3c18214 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem5.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem6.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem6.jpg new file mode 100644 index 00000000..42a7b3c8 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem6.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem7.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem7.jpg new file mode 100644 index 00000000..04660826 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem7.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem8.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem8.jpg new file mode 100644 index 00000000..d2f2b181 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem8.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem9.jpg b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem9.jpg new file mode 100644 index 00000000..2b438b2a Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/imgs/imagem9.jpg differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/extras/padding-testing.py b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/padding-testing.py new file mode 100644 index 00000000..30c80987 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/extras/padding-testing.py @@ -0,0 +1,58 @@ +""" +Pad ONNX example. +""" +import numpy as np + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +# Create inputs +x = helper.make_tensor_value_info('x', TensorProto.FLOAT, [8, 8]) +pads = helper.make_tensor_value_info('pads', TensorProto.INT64, [4]) + +# Create a node (Pad) with input/outputs +pad_node = helper.make_node( + 'Pad', + ['x', 'pads'], + ['x_padded'], + mode='constant' +) + +# Create the graph +graph_def = helper.make_graph( + [pad_node], + 'test-pad', + [x, pads], + [helper.make_tensor_value_info('x_padded', TensorProto.FLOAT, [1, 1, 4, 4])], +) + +onnx_model = helper.make_model(graph_def) + +# Let's freeze the opset. +del onnx_model.opset_import[:] +opset = onnx_model.opset_import.add() +opset.domain = '' +opset.version = 15 +onnx_model.ir_version = 8 + +# Verify the model +onnx.checker.check_model(onnx_model) + +# Print a human-readable representation of the graph +print(onnx.helper.printable_graph(onnx_model.graph)) + +# Do inference +sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + +# Initialize tensors +x = np.ones((8, 8), dtype=np.float32) +pads_values = [2,1,2,2] + +x_padded = sess.run(None, {'x': x, 'pads': pads_values})[0] + +print("Output:") +print(x_padded) diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/README.md b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/README.md new file mode 100644 index 00000000..7c9f9d8f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/README.md @@ -0,0 +1,93 @@ +# How to run +There are two main scripts, test_conv.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_conv.py you need to do +```bash +pytest test_conv.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_conv.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_conv.py + +# Hypothesis Tests Doubts for Conv operator + +## Conv + +### Inputs/attributes generation +What would be the best approach for generating inputs that satisfy the **X[C3]** constraint? + +We know that ideally Hypothesis should generate input values that already satisfied this equation. We have been searching for ways to do that and we found no viable way to do so. + +> (eric) I have not checked it very carefully, but Hypothesis supports several "backends", including one ("crosshair") that uses a SMT Solver. +> (eric later...) I have tried to use the backend (in a Google collab notebook), with no success. I have had no time to test it directly. Note also that [crosshair] is a standalone tool. But multiplying the tools is not a good idea in any case. +> So we have to stick to a handcrafted test generation strategy with filtering. That is what you've done and that seems fine to me! + +Therefore, our approach was to generate two unconstrained variables, and then derive the remaining two based on those, ensuring the final inputs satisfy the required condition. + +> (eric) Yes. This makes sense. The main difference with a strategy where the constraints would be solved resides in the fact that we have to process variables in a "smart" way in order to avoid generating too many invalid test cases. For instance if the constraint is $a+40xb<100$ with $a$ and $b$ in $[0,100]$, it is wise to draw $a$ first and solve for $b$, otherwise, you may generate a lot of invalid cases... And, in the worst case, you may well never exercise some values of $a$. Well, there is probably some strategy to be defined. +> (eric) To be honest, I am not completely convinced by this random testing approach if it does not really simply our work (i.e., find tricky test cases automatically). Our operators are simple and it does not seem very smart to generate 100 different tests for (e.g.,) $a+b$ with $a$ and and $b$ in $[0,1000] while we know that once you have tested it with all edge and corner cases plus a another non edge case, and all test pass, it is very likely that any test will pass too (hypothesis of equivalence classes). So, basically, the strategy should consist in (i) defining the equivalence classes, (ii) writing one test case per equivalence class, leaving random testing for problems where such equivalence class cannot not be defined easily (and we hope that random sampling will pick one element in each (undefined) class...) +> (eric) => We should probably have a plenary discussion about our test strategy... + +> (eric) btw, there is a custom-defined strategy called [hypothesis-torch](https://pypi.org/project/hypothesis-torch/). Might be useful (?). + + Another possibility was to generate only unconstrainted values and then filter by those who satisfy the equation (this would be computationally expensive and would probably defeat the purpose of using Hypothesis — many edge cases would be lost). + +> I agree. Filtering does not seem to be a good idea... + + +### Auto_Pad = "Valid" +There is no documentation stating how this type of configuration works. + +> (eric) In the doc, I read "VALID mean no padding". So I guess that this means the the size of the input tensor and the size of the kernel must be such that the kernel fits within the tensor. + +Therefore we are unable to calculate dY dimensions (useful for the generation phase) and we cannot reason about the results it produces. +How should we proceed? +Should we ignore this configuration for Auto_Pad or is there any documentation that we haven't looked yet. + +> (eric) No, you should consider it, but it simply corresponds to padding equal to zero at top, bottom, left, right... Notet that we may also reject this configuration considering that it is basically a non explicit padding. But let's consider it (since you have already basically treated this case). + +# Explanations of Conv corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +> (eric) In my mind, a "edge" case correspond to the extreme limit of an argument (its min and max). A "corner" case is the combination of edge cases for multiple parameters (hence the name "corner"). + +## Conv tests +For concat operator we checked the **corner and edge cases** for: **dx0, dx1, dx2, dx3, dw0, strides, auto_pad and pads**. +We think its **irrelevant** to check the corner and edge cases for **db0** because of **B [C1]** and **kernel shape** because of **W [C3]**. +We didn't check the corner and edges cases for **dw2, dw3 and dilations** because the way we generate them is not independent. + +> (eric) I agree, some combination of edge cases are not in the domain. For instance, if my constraint is x+y < 10 with x in [0,10] and y in [5,10], some corner cases are invalid. + +We consider that **dx0, dx1, and dw0** as lines since they are independent and we check the corner and edge cases for them. + +For **dx2** and **dx3** we consider them as a **rectangle**. We check each **corner** of the rectangle, a **middle point of each rectangle edge** and a **middle point of the rectangle**. + +> (eric) We can consider that those cases have already been "captured" by standard (non edge/corner) tests. + +For **pads** (when **auto_pad == NOTSET**) we consider a **4D cube** and we check each corner of the "4D cube". All **16 corners**, example, (min,min,min,min), (min,min,min,max), ... (min,max,max,max), (max,max,max,max) where min is the minimum value of pads and max is the maximum value of pads. +Also we check a middle point of each edge of the "4D cube" and a middle point of the "4D cube". + +For **auto_pad** we check if all the possible values are being generated (Except **VALID**) + +> (eric) Yes, but as written before, you may still consider "VALID". + +For **strides** we consider them as a **rectangle***. We check each **corner** of the rectangle, a **middle point of each rectangle edge** and a **middle point of the rectangle**. + +> (eric) Sounds good. +> Writing a few line on the overall testing approach would probably be useful. Another possibility would be to write a siple example (with the two python scripts, one for the standard tests and the other for the corner cases) that would serve both as a template and a documentation. +> BTW, I would have expected Hypothesis to capture some of the edge cases... \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/check_edges.py new file mode 100644 index 00000000..3c9d518f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/check_edges.py @@ -0,0 +1,230 @@ +import json +import itertools + + + +def check_edges(data): + with open("generated_data.json", "r") as f: + data = json.load(f) + check_dx0_result = check_individual_variables("dx0", data["dx0"], data["min_dx0"], data["max_dx0"]) + check_dx1_result = check_individual_variables("dx1", data["dx1"], data["min_dx1"], data["max_dx1"]) + check_x_spatial_axis_result = check_spatial_axis("x_spatial_axis", data["x_spatial_axis"], data["x_spatial_axis_min"], data["x_spatial_axis_max"]) + check_dw0_result = check_individual_variables("dw0", data["dw0"], data["min_dw0"], data["max_dw0"]) + # Irrelevant see dw1 because they are the same as dx1, X [C2] + check_w_spatial_axis_result = check_spatial_axis("w_spatial_axis",data["w_spatial_axis"], data["w_spatial_axis_min"], data["w_spatial_axis_max"]) + #Irrelevant see db0 because they are the same as dw0, B [C1] + strides_result = check_2d_variables("strides", data["strides"], data["strides_min"], data["strides_max"]) + auto_pad_result = check_auto_pad(data["auto_pad"]) + pads_result = check_pads(data["pads"], data["pads_min"], data["pads_max"]) + dilations_result = check_2d_variables("dilations", data["dilations"], data["dilation_min"], data["dilation_max"]) + #Irrelevant check kernel shape it would be the same as w_spatial_axis + return all([check_dx0_result, check_dx1_result, check_x_spatial_axis_result, strides_result, check_dw0_result, auto_pad_result, pads_result, dilations_result, check_w_spatial_axis_result]) + + +""" +Check individual variable analysis +""" +def check_individual_variables(variable_name, variable_analysis, min, max): + corner_cases = [min, max] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + + has_mean_case_mean = any(d for d in variable_analysis if d > min and d < max) + has_out_of_bounds = not(any(d for d in variable_analysis if d < min or d > max)) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(f" - Missing mean case") + if not has_out_of_bounds: + print(f" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + +""" +Check spatial_axis contains all corner cases +""" +def check_spatial_axis(testcase_name, spatial_axis, min_spatial_axis, max_spatial_axis): + + corner_cases = generate_corner_cases(min_spatial_axis, max_spatial_axis) + corner_cases_test = all(corner_case in spatial_axis for corner_case in corner_cases) + has_edge_case_lower = has_edge_case(spatial_axis, 0, min_spatial_axis, min_spatial_axis, max_spatial_axis) + has_edge_case_higher = has_edge_case(spatial_axis, 0, max_spatial_axis, min_spatial_axis, max_spatial_axis) + has_edge_case_lower_1 = has_edge_case(spatial_axis, 1, min_spatial_axis, min_spatial_axis, max_spatial_axis) + has_edge_case_higher_1 = has_edge_case(spatial_axis, 1, max_spatial_axis, min_spatial_axis, max_spatial_axis) + has_mean_case_mean = has_mean_case(spatial_axis, min_spatial_axis, max_spatial_axis) + + print(f"{testcase_name} analysis:") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in spatial_axis: + print(f" - Missing corner case: {corner_case}") + if not has_edge_case_lower: + print(f" - Missing edge case lower in dimension 0") + if not has_edge_case_higher: + print(f" - Missing edge case higher in dimension 0") + if not has_edge_case_lower_1: + print(f" - Missing edge case lower in dimension 1") + if not has_edge_case_higher_1: + print(f" - Missing edge case higher in dimension 1") + if not has_mean_case_mean: + print(f" - Missing mean case") + + + return all([corner_cases_test, has_edge_case_lower, has_edge_case_higher, has_edge_case_lower_1, has_edge_case_higher_1, has_mean_case_mean]) + +""" +Check pads contains all corner cases +""" +def check_pads(pads, min_pads, max_pads): + #Dont check cases where auto_pad is different from NOTSET + pads = [pad for pad in pads if len(pad) == 4] + + corner_cases_combinations = [list(t) for t in itertools.product([min_pads, max_pads], repeat=4)] + corner_cases_test = all(corner_case in pads for corner_case in corner_cases_combinations) + if not corner_cases_test: + print(f"pads analysis:") + for corner_case in corner_cases_combinations: + if corner_case not in pads: + print(f" - Missing corner case: {corner_case}") + + mean = 'A' + edge_cases_combinations = [list(t) for t in itertools.product([min_pads, max_pads, mean], repeat=4) if mean in t] + edge_cases_tests = pads_wildcard(edge_cases_combinations, pads, mean, min_pads, max_pads) + if not edge_cases_tests: + for edge_case in edge_cases_combinations: + if not any(match_wildcard(edge_case, pad, mean, min_pads, max_pads) for pad in pads): + print(f" - Missing edge case with wildcard: {edge_case}") + + return all([corner_cases_test, edge_cases_tests]) + +""" +Check strides contains all corner cases +""" + +def check_2d_variables(variable_name, variable_value, min_strides, max_strides): + + corner_cases = generate_corner_cases(min_strides, max_strides) + corner_cases_test = all(corner_case in variable_value for corner_case in corner_cases) + + # Check for lines + has_mean_case_mean_lines = any( + d for d in variable_value if len(d) > 0 and d[0] > min_strides and d[0] < max_strides + ) + has_out_of_bounds_lines = not(any( + d for d in variable_value if len(d) > 0 and (d[0] < min_strides or d[0] > max_strides) + )) + + # Edge cases + has_edge_case_lower = has_edge_case(variable_value, 0, min_strides, min_strides, max_strides) + has_edge_case_higher = has_edge_case(variable_value, 0, max_strides, min_strides, max_strides) + + #Check for columns + has_mean_case_mean_columns = any( + d for d in variable_value if len(d) > 1 and d[1] > min_strides and d[1] < max_strides + ) + has_out_of_bounds_columns = not(any( + d for d in variable_value if len(d) > 1 and (d[1] < min_strides or d[1] > max_strides) + )) + + # Edge cases + has_edge_case_lower_1 = has_edge_case(variable_value, 1, min_strides, min_strides, max_strides) + has_edge_case_higher_1 = has_edge_case(variable_value, 1, max_strides, min_strides, max_strides) + + print(f"{variable_name} analysis:") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_value: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean_lines: + print(f" - Missing mean case in lines") + if not has_out_of_bounds_lines: + print(f" - Found out of bounds values in lines") + if not has_mean_case_mean_columns: + print(f" - Missing mean case in columns") + if not has_out_of_bounds_columns: + print(f" - Found out of bounds values in columns") + if not has_edge_case_lower: + print(f" - Missing edge case lower in dimension 0") + if not has_edge_case_higher: + print(f" - Missing edge case higher in dimension 0") + if not has_edge_case_lower_1: + print(f" - Missing edge case lower in dimension 1") + if not has_edge_case_higher_1: + print(f" - Missing edge case higher in dimension 1") + + return all([corner_cases_test, has_mean_case_mean_lines, has_out_of_bounds_lines, has_mean_case_mean_columns, has_out_of_bounds_columns, has_edge_case_lower, has_edge_case_higher, has_edge_case_lower_1, has_edge_case_higher_1]) + + +""" +Check auto_pad contains all values +""" +def check_auto_pad(auto_pad): + possible_values = ['SAME_UPPER', 'SAME_LOWER', 'NOTSET', 'VALID'] + auto_pad_test = all(value in auto_pad for value in possible_values) + auto_pad_out_of_bounds = not any(value not in possible_values for value in auto_pad) + + print(f"auto_pad analysis:") + if not auto_pad_test: + for value in possible_values: + if value not in auto_pad: + print(f" - Missing value: {value}") + if not auto_pad_out_of_bounds: + print(f" - Found out of bounds values") + + return all([auto_pad_test, auto_pad_out_of_bounds]) + +""" +Generate corner cases for x_spatial_axis +""" +def generate_corner_cases(min_x_spatial_axis, max_x_spatial_axis): + return [ + [x1, x2] + for x1 in (min_x_spatial_axis, max_x_spatial_axis) + for x2 in (min_x_spatial_axis, max_x_spatial_axis) + ] + + +""" +Auxiliary functions to help generate edges and corner cases +""" +def has_edge_case(x_spatial_axis, dim_idx, edge_value, min_value, max_value): + return any( + edge_case + for edge_case in x_spatial_axis + if len(edge_case) > 0 and edge_case[dim_idx] == edge_value + and all( + edge_case[i] != min_value and edge_case[i] != max_value + for i in range(len(edge_case)) if i != dim_idx + ) + ) + +def has_mean_case(x_spatial_axis, min_value, max_value): + return any( + edge_case + for edge_case in x_spatial_axis + if all(v != min_value and v != max_value for v in edge_case) + ) + + +def match_wildcard(edge_case, pad, wildcard_value, min_value, max_value): + for ec, p in zip(edge_case, pad): + if ec != wildcard_value: + if ec != p: + return False + else: + if p == min_value or p == max_value: + return False + return True + + +def pads_wildcard(edge_cases, pads, wildcard_value, min_value, max_value): + for edge_case in edge_cases: + if not any(match_wildcard(edge_case, pad, wildcard_value, min_value, max_value) for pad in pads): + return False + return True + +print(check_edges("generated_data.json")) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/requirements.txt new file mode 100644 index 00000000..329072ae --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/requirements.txt @@ -0,0 +1,5 @@ +pytest +numpy +hypothesis +onnx +onnxruntime \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/test_conv.py b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/test_conv.py new file mode 100644 index 00000000..9dd963e0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/conv/hypothesis/test_conv.py @@ -0,0 +1,444 @@ +import math +import numpy as np +import os +import json + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper, TensorProto +import onnx.checker +from onnxruntime import InferenceSession + + +AUTO_PAD_OPTIONS = ["NOTSET", "SAME_UPPER", "SAME_LOWER", "VALID"] #auto_pad [C1] + +""" +Tensor value details +""" +tensor_range = { + 'min_value': -1e6, + 'max_value': 1e6, + 'allow_nan': False, + 'allow_infinity': False +} + + +""" +Inputs/attributes details +""" +inputs_atributes = { + 'min_dx0': 1, # Dimension should always be positive (batch size > 0) + 'max_dx0': 10, # Adjust as needed + 'min_dx1': 1, # Dimension should always be positive (channels > 0) + 'max_dx1': 10, # Adjust as needed + 'x_spatial_axis_min': 1, # Dimension should always be positive (spatial axes > 0) + 'x_spatial_axis_max': 10, # Adjust as needed + 'min_dw0': 1, # Dimension should always be positive (output channels > 0) + 'max_dw0': 10, # Adjust as needed + 'w_spatial_axis_min': 1, # Dimension should always be positive (spatial axes > 0) + 'w_spatial_axis_max': 10, # Adjust as needed + 'pads_min': 0, # Pads [C1] + 'pads_max': 10, # Adjust as needed + 'strides_min': 1, # Strides [C1] + 'strides_max': 10, # Adjust as needed + 'dilation_min': 1, # Dilations [C1] + 'dilation_max': 10, # Used when auto_pad is not NOTSET + 'groups': [] +} + + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Store generated data +""" +generated_data = { + "dx0": [], + "dx1": [], + "x_spatial_axis": [], + "x": [], + "dw0": [], + "dw1": [], + "w_spatial_axis": [], + "w": [], + "db0": [], + "bias": [], + "strides": [], + "auto_pad": [], + "pads": [], + "dilations": [], + "kernel_shape": [], + "dy2": [], + "dy3": [], + "groups": [] +} + + +""" +Function to generate valid convolution arguments +""" +@settings(backend='crosshair') +@st.composite +def valid_conv_args(draw, inputs_atributes=inputs_atributes, tensor_range=tensor_range): + float_strategy = st.floats(min_value=tensor_range['min_value'], max_value=tensor_range['max_value'], + allow_nan=tensor_range['allow_nan'], allow_infinity=tensor_range['allow_infinity']) + + dx0 = draw(st.integers(min_value=inputs_atributes['min_dx0'], + max_value=inputs_atributes['max_dx0'])) + + dx1 = draw(st.integers(min_value=inputs_atributes['min_dx1'], + max_value=inputs_atributes['max_dx1'])) + + + # X [C1] + num_spatial_axes_x = 2 + spatial_x_dimension_values = st.integers(min_value=inputs_atributes['x_spatial_axis_min'], + max_value=inputs_atributes['x_spatial_axis_max']) + x_spatial_axis = draw(st.lists + (spatial_x_dimension_values, + min_size=num_spatial_axes_x, + max_size=num_spatial_axes_x)) + # X [C4] + x = draw(hnp.arrays(dtype=np.float32, shape=(dx0, dx1, *x_spatial_axis), + elements=float_strategy)) + + # Strides [C1] + stride_value = st.integers(min_value=inputs_atributes['strides_min'], max_value=inputs_atributes['strides_max']) + # Strides [C2] + stride_axis_number = 2 + strides = draw(st.lists(stride_value, min_size=stride_axis_number, max_size=stride_axis_number)) + + # Groups [C1] + # Groups [C2] + # Groups [C3] + VALID_GROUPS = [1, dx1] + groups = draw(st.sampled_from(VALID_GROUPS)) + inputs_atributes['groups'].append(groups) + + # W [C5] + VALID_VALUES_DW0 = [i for i in range(inputs_atributes['min_dw0'], inputs_atributes['max_dw0'] + 1) if i % groups == 0] + dw0 = draw(st.sampled_from(VALID_VALUES_DW0)) + + # W [C1] -> X [C2] #This value does not need to be generated, by X [C2] constraint + dw1 = dx1 // groups + + num_spatial_axes_w = 2 + + # Auto Pad [C1] + auto_pad = draw(st.sampled_from(AUTO_PAD_OPTIONS)) + # Auto Pad [C2] + if auto_pad == "NOTSET" or auto_pad == "VALID": + if auto_pad == "NOTSET": + # Pads [C1] + pads_value = st.integers(min_value=inputs_atributes['pads_min'], max_value=inputs_atributes['pads_max']) + # Pads [C2] + pads = draw(st.lists( + pads_value, + min_size=2*num_spatial_axes_x, + max_size=2*num_spatial_axes_x + )) + if auto_pad == "VALID": + pads = [0 for _ in range(2 * num_spatial_axes_x)] + + + alpha = pads[0] + pads[2] + x_spatial_axis[0] + beta = pads[1] + pads[3] + x_spatial_axis[1] + w_spatial_axis = [draw(st.integers(min_value=inputs_atributes['w_spatial_axis_min'], + max_value=inputs_atributes['w_spatial_axis_max'])) for i in range(num_spatial_axes_w)] + + # Dilations [C1] + # Dilations [C2] + dilations = [draw(st.integers + (min_value=inputs_atributes['dilation_min'], + max_value=inputs_atributes['dilation_max'])) for _ in range(num_spatial_axes_w)] + + theta = (dilations[0] * (w_spatial_axis[0] - 1)) + 1 + gamma = (dilations[1] * (w_spatial_axis[1] - 1)) + 1 + + # y spatial dimension calculations + # When auto_pad is NOTSET, pads are explicit + # W [C2] -> X [C3], Strides [C2] -> X [C3], Pads [C3] -> X [C3], Dilations [C3] -> X [C3], Y [C1] -> X [C3] + dy2 = math.floor(((alpha - (theta)) / strides[0])) + 1 + dy3 = math.floor(((beta - (gamma)) / strides[1])) + 1 + assume (dy2 > 0) + assume (dy3 > 0) + + else: + pads = [] + dilations = [] + spatial_w_dimension_values = st.integers(min_value=inputs_atributes['w_spatial_axis_min'], + max_value=inputs_atributes['w_spatial_axis_max']) + w_spatial_axis = draw(st.lists + (spatial_w_dimension_values, + min_size=num_spatial_axes_w, + max_size=num_spatial_axes_w)) + + # y spatial dimension calculations + # When auto_pad is different from NOTSET + # W [C2] -> X [C3], Strides [C2] -> X [C3], Pads [C3] -> X [C3], Dilations [C3] -> X [C3], Y [C1] -> X [C3] + dy2 = math.ceil(x_spatial_axis[0] / strides[0]) + dy3 = math.ceil(x_spatial_axis[1] / strides[1]) + assume (dy2 > 0) + assume (dy3 > 0) + + # W [C4] + w = draw(hnp.arrays(dtype=np.float32, shape=(dw0, dw1, *w_spatial_axis), + elements=float_strategy)) + + # B [C1] # This value does not need to be generated, once it is always equal to dw0 + db0 = dw0 + bias = draw(hnp.arrays(dtype=np.float32, shape=(db0,), + elements=float_strategy)) + + # Kernel Shape [C1] + # Kernel Shape [C2] -> W [C3] + # This data is not need to be generated, once it is always equal to w_spatial_axis + kernel_shape = w_spatial_axis + + + return dx0, dx1, x_spatial_axis, x, dw0, dw1, w_spatial_axis, w, db0, bias, strides, auto_pad, pads, dilations, kernel_shape, dy2, dy3, groups + + +""" +Function that runs the test +""" +@settings(max_examples=1000, backend='crosshair') +@given(valid_conv_args()) +def test_conv(args): + dx0, dx1, x_spatial_axis, x, dw0, dw1, w_spatial_axis, w, db0, bias, strides, auto_pad, pads, dilations, kernel_shape, dy2, dy3, groups = args + generated_data["dx0"].append(dx0) + generated_data["dx1"].append(dx1) + generated_data["x_spatial_axis"].append(x_spatial_axis) + generated_data["x"].append(x.tolist()) + generated_data["dw0"].append(dw0) + generated_data["dw1"].append(dw1) + generated_data["w_spatial_axis"].append(w_spatial_axis) + generated_data["w"].append(w.tolist()) + generated_data["db0"].append(db0) + generated_data["bias"].append(bias.tolist()) + generated_data["strides"].append(strides) + generated_data["auto_pad"].append(auto_pad) + generated_data["pads"].append(pads) + generated_data["dilations"].append(dilations) + generated_data["kernel_shape"].append(kernel_shape) + generated_data["dy2"].append(dy2) + generated_data["dy3"].append(dy3) + generated_data["groups"].append(groups) + + y, node_def = run_onnx_conv(dx0, dx1, x_spatial_axis, x, + dw0, dw1, w_spatial_axis, w, + db0, bias,strides, auto_pad, + pads, dilations, kernel_shape, dy2, dy3, groups) + + check_constraints(x, w, auto_pad, y, dy2, + dy3, kernel_shape, bias, strides, + node_def, pads, dilations, groups) + + +""" +Function to write generated data to a json file +""" +def teardown_module(): + data = { + "titulo": "Data generated by Hypothesis for ONNX Conv Operation", + "min_dx0": inputs_atributes['min_dx0'], + "max_dx0": inputs_atributes['max_dx0'], + "dx0": generated_data["dx0"], + "min_dx1": inputs_atributes['min_dx1'], + "max_dx1": inputs_atributes['max_dx1'], + "dx1": generated_data["dx1"], + "x_spatial_axis_min": inputs_atributes['x_spatial_axis_min'], + "x_spatial_axis_max": inputs_atributes['x_spatial_axis_max'], + "x_spatial_axis": generated_data["x_spatial_axis"], + "x": generated_data["x"], + "min_dw0": inputs_atributes['min_dw0'], + "max_dw0": inputs_atributes['max_dw0'], + "dw0": generated_data["dw0"], + "w_spatial_axis_min": inputs_atributes['w_spatial_axis_min'], + "w_spatial_axis_max": inputs_atributes['w_spatial_axis_max'], + "w_spatial_axis": generated_data["w_spatial_axis"], + "w": generated_data["w"], + "db0": generated_data["db0"], + "bias": generated_data["bias"], + "strides_min": inputs_atributes['strides_min'], + "strides_max": inputs_atributes['strides_max'], + "strides": generated_data["strides"], + "auto_pad": generated_data["auto_pad"], + "pads_min": inputs_atributes['pads_min'], + "pads_max": inputs_atributes['pads_max'], + "pads": generated_data["pads"], + "dilation_min": inputs_atributes['dilation_min'], + "dilation_max": inputs_atributes['dilation_max'], + "dilations": generated_data["dilations"], + "kernel_shape": generated_data["kernel_shape"], + "dy2": generated_data["dy2"], + "dy3": generated_data["dy3"] + } + + with open("generated_data.json", "w") as f: + json.dump(data, f, indent=4) + + +""" +Function that runs the ONNX Conv operation +""" +def run_onnx_conv(dx0, dx1, x_spatial_axis, x, + dw0, dw1, w_spatial_axis, w, + db0, bias, strides, auto_pad, + pads, dilations, kernel_shape, dy2, dy3, groups): + + x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, x_spatial_axis[0], x_spatial_axis[1]]) + w_onnx = helper.make_tensor_value_info('w_onnx', TensorProto.FLOAT, [dw0, dw1, w_spatial_axis[0], w_spatial_axis[1]]) + b_onnx = helper.make_tensor_value_info('b_onnx', TensorProto.FLOAT, [dw0]) + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilations, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + group=groups, + ) + elif auto_pad == "SAME_UPPER" or auto_pad == "SAME_LOWER": + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + kernel_shape=kernel_shape, + strides=strides, + auto_pad=auto_pad, + group=groups, + ) + else: #auto_pad == "VALID" + node_def = helper.make_node( + 'Conv', + ['x_onnx', 'w_onnx', 'b_onnx'], + ['y_Onnx'], + dilations=dilations, + kernel_shape=kernel_shape, + strides=strides, + auto_pad='VALID', + group=groups, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-conv', + [x_onnx, w_onnx, b_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dw0, dy2, dy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + # Initialize tensors + x = x.reshape(dx0, dx1, x_spatial_axis[0], x_spatial_axis[1]).astype(np.float32) + w = w.reshape(dw0, dw1, w_spatial_axis[0], w_spatial_axis[1]).astype(np.float32) + b = bias.reshape((dw0,)).astype(np.float32) + + y = sess.run(None, {'x_onnx': x, 'w_onnx':w, 'b_onnx': b})[0] + + print("Output shape:", y.shape) + print("Output values:", y) + return y, node_def + + +""" +Function that defines asserts for the constraints +""" +def check_constraints(x, w, auto_pad, y, dy2, + dy3, kernel_shape, bias, strides, + node_def, pads, dilation, groups): + + # X Constraints + # X [C1] + assert x.ndim == 4 and x.shape[0] > 0 and x.shape[1] > 0 and x.shape[2] > 0 and x.shape[3] > 0 + # X [C2] + assert x.shape[1] == w.shape[1] * groups + # X [C3] + assert y.shape[2] == dy2 and y.shape[3] == dy3 + + # W Constraints + # W [C1] -> X [C2] + # W [C2] -> X [C3] + # W [C3] + kernel_shape[0] == w.shape[2] and kernel_shape[1] == w.shape[3] + # W [C4] + assert w.ndim == 4 + assert all(dim > 0 for dim in w.shape) + # W [C5] + assert w.shape[0] % groups == 0 + + + # B Constraints + # B [C1] + assert bias.shape[0] == w.shape[0] + + # Strides Constraints + # S [C1] + assert all(s > 0 for s in strides) + # S [C2] + assert len(strides) == 2 + # S [C2] -> X [C3] + + + # Auto_pad Constraints + # auto_pad [C1] + assert auto_pad in AUTO_PAD_OPTIONS + # auto_pad [C2] + if auto_pad != "NOTSET": + assert all(attr.name!="pads" for attr in node_def.attribute) + + if auto_pad == "NOTSET": + # Pads Constraints + # Pads [C1] + assert all(p >= 0 for p in pads) + # Pads [C2] + assert len(pads) == (x.ndim - 2) * 2 + # Pads [C3] -> X [C3] + + # Dilation - Constraints + # Dilation [C1] + assert all(d > 0 for d in dilation) + # Dilation [C2] + assert len(dilation) == (w.ndim - 2) + # Dilation [C3] -> X [C3] + + # Groups - Constraints + # Groups [C1] + assert groups > 0 + # Groups [C2] + assert x.shape[1] % groups == 0 + assert y.shape[1] % groups == 0 + # Groups [C3] + assert groups == 1 or groups == x.shape[1] + + # Kernel shape - Constraints + # Kernel shape [C1] + assert all(k > 0 for k in kernel_shape) + # Kernel shape [C2] -> W [C3] + + # Y - Constraints + # Y [C1] -> X [C3] diff --git a/safety-related-profile/sonnx/ops/spec/tests/div/README.md b/safety-related-profile/sonnx/ops/spec/tests/div/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/div/div_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/div/div_doc.ipynb new file mode 100644 index 00000000..35bf252f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/div/div_doc.ipynb @@ -0,0 +1,197 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Requirement already satisfied: onnx in /usr/local/lib/python3.11/dist-packages (1.17.0)\n", + "Requirement already satisfied: onnxruntime in /usr/local/lib/python3.11/dist-packages (1.20.1)\n", + "Requirement already satisfied: numpy>=1.20 in /usr/local/lib/python3.11/dist-packages (from onnx) (1.26.4)\n", + "Requirement already satisfied: protobuf>=3.20.2 in /usr/local/lib/python3.11/dist-packages (from onnx) (4.25.6)\n", + "Requirement already satisfied: coloredlogs in /usr/local/lib/python3.11/dist-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /usr/local/lib/python3.11/dist-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from onnxruntime) (24.2)\n", + "Requirement already satisfied: sympy in /usr/local/lib/python3.11/dist-packages (from onnxruntime) (1.13.1)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /usr/local/lib/python3.11/dist-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "\n", + "!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "input2_name = \"Y\"\n", + "div_output_name = \"DivOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2 rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensors (two inputs for division)\n", + "input1 = onnx.helper.make_tensor_value_info(input1_name, onnx.TensorProto.FLOAT, [None, None])\n", + "input2 = onnx.helper.make_tensor_value_info(input2_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after div operation)\n", + "div_output = onnx.helper.make_tensor_value_info(div_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define division node: Z = X / Y\n", + "div_node = onnx.helper.make_node(\"Div\", [input1_name, input2_name], [div_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[div_node],\n", + " name=\"Div\",\n", + " inputs=[input1, input2],\n", + " outputs=[div_output]\n", + ")\n", + "\n", + "# Create the ONNX model\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"div.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"div.onnx\")\n", + "\n", + "def do_div(x,y):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x, input2_name: y})\n", + "\n", + " x_f=(np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f=(np.array2string(y, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f=(np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}, Y={y_f}\")\n", + " print(f\"Result: X/Y={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],[3.0, 4.0]], dtype=np.float32)\n", + "y = np.array([[3.0, 4.0],[5.0, 6.0]], dtype=np.float32)\n", + "\n", + "# Run inference\n", + "do_div(x,y)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan and inf values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v=[1, 0, float(\"inf\"), float(\"nan\")]\n", + "\n", + "for x in v:\n", + " for y in v:\n", + " x_np = np.array([[x],[x]], dtype=np.float32)\n", + " y_np = np.array([[y],[y]], dtype=np.float32)\n", + " do_div(x_np,y_np)\n", + "\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "execution_count": 46, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "X=[[1.00000000000000000000000000000000,2.00000000000000000000000000000000], [3.00000000000000000000000000000000,4.00000000000000000000000000000000]], Y=[[3.00000000000000000000000000000000,4.00000000000000000000000000000000], [5.00000000000000000000000000000000,6.00000000000000000000000000000000]]\n", + "Result: X/Y=[[0.33333334326744079589843750000000,0.50000000000000000000000000000000], [0.60000002384185791015625000000000,0.66666668653488159179687500000000]]\n", + "X=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]], Y=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]]\n", + "Result: X/Y=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]]\n", + "X=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]], Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "Result: X/Y=[[inf], [inf]]\n", + "X=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]], Y=[[inf], [inf]]\n", + "Result: X/Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "X=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]], Y=[[nan], [nan]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]], Y=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]]\n", + "Result: X/Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "X=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]], Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]], Y=[[inf], [inf]]\n", + "Result: X/Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "X=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]], Y=[[nan], [nan]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[inf], [inf]], Y=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]]\n", + "Result: X/Y=[[inf], [inf]]\n", + "X=[[inf], [inf]], Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "Result: X/Y=[[inf], [inf]]\n", + "X=[[inf], [inf]], Y=[[inf], [inf]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[inf], [inf]], Y=[[nan], [nan]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[nan], [nan]], Y=[[1.00000000000000000000000000000000], [1.00000000000000000000000000000000]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[nan], [nan]], Y=[[0.00000000000000000000000000000000], [0.00000000000000000000000000000000]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[nan], [nan]], Y=[[inf], [inf]]\n", + "Result: X/Y=[[nan], [nan]]\n", + "X=[[nan], [nan]], Y=[[nan], [nan]]\n", + "Result: X/Y=[[nan], [nan]]\n" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/exp/README.md b/safety-related-profile/sonnx/ops/spec/tests/exp/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/exp/exp_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/exp/exp_doc.ipynb new file mode 100644 index 00000000..787cdd0a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/exp/exp_doc.ipynb @@ -0,0 +1,283 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "Result: exp(X)=[[ 2.71828175, 7.38905621], [20.08553696,54.59814835]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: exp(X)=[[2.71828175], [2.71828175]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: exp(X)=[[1.00000000], [1.00000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: exp(X)=[[0.36787945], [0.36787945]]\n", + "X=[[inf], [inf]]\n", + "Result: exp(X)=[[inf], [inf]]\n", + "X=[[-inf], [-inf]]\n", + "Result: exp(X)=[[0.00000000], [0.00000000]]\n", + "X=[[nan], [nan]]\n", + "Result: exp(X)=[[nan], [nan]]\n", + "\n", + "## Example 1\n", + "X=[[ 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: exp(X)=[[1.00000000,2.71828175,0.36787945]]\n", + "\n", + "## Example 2\n", + "X=[[-2.00000000, 0.00000000], [ 1.00000000, 2.00000000], [-4.00000000, 4.00000000]]\n", + "Result: exp(X)=[[1.35335281e-01,1.00000000e+00], [2.71828175e+00,7.38905621e+00], [1.83156393e-02,5.45981483e+01]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan,-inf]]\n", + "Result: exp(X)=[[ inf, nan,0.00000000]]\n", + "\n", + "--- Test pour FP16 input range ---\n", + "Max FP16: 65504.0\n", + "Min FP16 pour exp(x): -65504.0\n", + "exp(-65504.0) pour FP16: 0.0\n", + "ln(65504.0) pour FP16: 11.09375\n", + "\n", + "--- Test pour FP32 input range ---\n", + "Max FP32: 3.4028234663852886e+38\n", + "Min FP32 pour exp(x): -3.4028234663852886e+38\n", + "exp(-3.4028234663852886e+38) pour FP32: 0.0\n", + "ln(3.4028234663852886e+38) pour FP32: 88.72283935546875\n", + "\n", + "--- Test pour FP64 input range ---\n", + "Max FP64: 1.7976931348623157e+308\n", + "Min FP64 pour exp(x): -1.7976931348623157e+308\n", + "exp(-1.7976931348623157e+308) pour FP64: 0.0\n", + "ln(1.7976931348623157e+308) pour FP64: 709.782712893384\n", + "\n", + "Input tensor.\n", + "*FP16*: the input range for non +Inf values of `Y` is defined by $[-65504.0, ln(65504.0)] = [-65504.0, 11.09375]$.\n", + "*FP32*: the input range for non +Inf values of `Y` is defined by $[-3.4028234663852886e+38, ln(3.4028234663852886e+38)] = [-3.4028234663852886e+38, 88.72283935546875]$.\n", + "*FP64*: the input range for non +Inf values of `Y` is defined by $[-1.7976931348623157e+308, ln(1.7976931348623157e+308)] = [-1.7976931348623157e+308, 709.782712893384]$.\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "exp_output_name = \"ExpOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after exp operation)\n", + "exp_output = onnx.helper.make_tensor_value_info(exp_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define Exp node: Y = Exp(X)\n", + "exp_node = onnx.helper.make_node(\"Exp\", [input_name], [exp_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[exp_node],\n", + " name=\"Exp\",\n", + " inputs=[input_tensor],\n", + " outputs=[exp_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Exp is available since early opsets, 13 is safe)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"exp.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"exp.onnx\")\n", + "\n", + "def do_exp(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: exp(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],\n", + " [3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_exp(x)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_exp(x_np)\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [0, 1, -1]\n", + "x_example_1 = np.array([[0.0, 1.0, -1.0]], dtype=np.float32)\n", + "do_exp(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[ -2 0 ]\n", + "# [ 1 2 ]\n", + "# [ -4 4 ]]\n", + "x_example_2 = np.array([\n", + " [-2.0, 0.0],\n", + " [1.0, 2.0],\n", + " [-4.0, 4.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_exp(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# X =\n", + "# [[ inf nan -inf]\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\")]\n", + "], dtype=np.float32)\n", + "\n", + "do_exp(x_example_3)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "# --- Test pour FP16 input range ---\n", + "print(\"\\n--- Test pour FP16 input range ---\")\n", + "max_fp16 = np.finfo(np.float16).max\n", + "min_fp16 = np.finfo(np.float16).min\n", + "print(f\"Max FP16: {max_fp16}\")\n", + "print(f\"Min FP16 pour exp(x): {min_fp16}\")\n", + "exp_min_fp16 = np.exp(min_fp16)\n", + "print(f\"exp({min_fp16}) pour FP16: {exp_min_fp16}\")\n", + "ln_max_fp16 = np.log(max_fp16)\n", + "print(f\"ln({max_fp16}) pour FP16: {ln_max_fp16}\")\n", + "\n", + "# --- Test pour FP32 input range ---\n", + "print(\"\\n--- Test pour FP32 input range ---\")\n", + "max_fp32 = np.finfo(np.float32).max\n", + "min_fp32 = np.finfo(np.float32).min\n", + "print(f\"Max FP32: {max_fp32}\")\n", + "print(f\"Min FP32 pour exp(x): {min_fp32}\")\n", + "exp_min_fp32 = np.exp(min_fp32)\n", + "print(f\"exp({min_fp32}) pour FP32: {exp_min_fp32}\")\n", + "ln_max_fp32 = np.log(max_fp32)\n", + "print(f\"ln({max_fp32}) pour FP32: {ln_max_fp32}\")\n", + "\n", + "# --- Test pour FP64 input range ---\n", + "print(\"\\n--- Test pour FP64 input range ---\")\n", + "max_fp64 = np.finfo(np.float64).max\n", + "min_fp64 = np.finfo(np.float64).min\n", + "print(f\"Max FP64: {max_fp64}\")\n", + "print(f\"Min FP64 pour exp(x): {min_fp64}\")\n", + "exp_min_fp64 = np.exp(min_fp64)\n", + "print(f\"exp({min_fp64}) pour FP64: {exp_min_fp64}\")\n", + "ln_max_fp64 = np.log(max_fp64)\n", + "print(f\"ln({max_fp64}) pour FP64: {ln_max_fp64}\")\n", + "\n", + "\n", + "\n", + "# --- Affichage des ranges ---\n", + "print(f\"\\nInput tensor.\")\n", + "print(f\"*FP16*: the input range for non +Inf values of `Y` is defined by $[-65504.0, ln(65504.0)] = [-65504.0, 11.09375]$.\")\n", + "print(f\"*FP32*: the input range for non +Inf values of `Y` is defined by $[-3.4028234663852886e+38, ln(3.4028234663852886e+38)] = [-3.4028234663852886e+38, 88.72283935546875]$.\")\n", + "print(f\"*FP64*: the input range for non +Inf values of `Y` is defined by $[-1.7976931348623157e+308, ln(1.7976931348623157e+308)] = [-1.7976931348623157e+308, 709.782712893384]$.\")\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python (.venv)", + "language": "python", + "name": "venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.ipynb b/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.ipynb new file mode 100644 index 00000000..53d4ec73 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.ipynb @@ -0,0 +1,492 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "245013fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "flatten_output_name = \"Y\"\n", + "\n", + "# Create the ONNX model with Flatten operator\n", + "def create_flatten_model(axis, input_rank, output_shape, dtype):\n", + "\n", + " #Create input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_rank)\n", + "\n", + " # Create output tensor (final result after flatten operation)\n", + " flatten_output = onnx.helper.make_tensor_value_info(flatten_output_name, dtype, output_shape)\n", + "\n", + " # Define flatten node\n", + " flatten_node = onnx.helper.make_node(\n", + " \"Flatten\",\n", + " inputs=[input1_name],\n", + " outputs=[flatten_output_name],\n", + " axis=axis\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [flatten_node],\n", + " \"Flatten\",\n", + " [input1],\n", + " [flatten_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)]) # Explicitly set opset to 22\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"flatten.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"flatten.onnx\")\n", + " return session\n", + "\n", + "def do_flatten(x, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(\"Shape of input tensor:\", x.shape)\n", + " print(f\"X={x_f}\")\n", + " print(\"Shape of output tensor:\", output[0].shape)\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fda05307", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1, 24)\n", + "Result = [[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23]]\n" + ] + } + ], + "source": [ + "# Case N1: 3-rank tensor (int32), axis=0\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = 0\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8e29b734", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (2, 12)\n", + "Result = [[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11], [12,13,14,15,16,17,18,19,20,21,22,23]]\n" + ] + } + ], + "source": [ + "# Case N2: 3-rank tensor (int32), axis=1\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = 1\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (6, 4)\n", + "Result = [[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11], [12,13,14,15], [16,17,18,19], [20,21,22,23]]\n" + ] + } + ], + "source": [ + "# Case N3: 3-rank tensor (int32), axis=2\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = 2\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "ac8072e7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (24, 1)\n", + "Result = [[ 0], [ 1], [ 2], [ 3], [ 4], [ 5], [ 6], [ 7], [ 8], [ 9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20], [21], [22], [23]]\n" + ] + } + ], + "source": [ + "# Case N4: 3-rank tensor (int32), axis=3\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = 3\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "19649d0a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (6, 4)\n", + "Result = [[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11], [12,13,14,15], [16,17,18,19], [20,21,22,23]]\n" + ] + } + ], + "source": [ + "# Case N4: 3-rank tensor (int32), axis=-1\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = -1\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "edd54c7a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (2, 12)\n", + "Result = [[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11], [12,13,14,15,16,17,18,19,20,21,22,23]]\n" + ] + } + ], + "source": [ + "# Case N4: 3-rank tensor (int32), axis=-2\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axis = -2\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)]\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "c37c4383", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Case E1: Empty Tensor (shape 2,0,4) ---\n", + "Shape of input tensor: (2, 0, 4)\n", + "X=[]\n", + "Shape of output tensor: (1, 0)\n", + "Result = []\n", + "--- Case E1: Empty Tensor (shape 2,0,4) ---\n", + "Shape of input tensor: (2, 0, 4)\n", + "X=[]\n", + "Shape of output tensor: (2, 0)\n", + "Result = []\n", + "--- Case E1: Empty Tensor (shape 2,0,4) ---\n", + "Shape of input tensor: (2, 0, 4)\n", + "X=[]\n", + "Shape of output tensor: (0, 4)\n", + "Result = []\n", + "\n", + "--- Case E2: Axis 0 (dY0 is null/empty range -> 1) ---\n", + "Shape of input tensor: (2, 3)\n", + "X=[[0,1,2], [3,4,5]]\n", + "Shape of output tensor: (1, 6)\n", + "Result = [[0,1,2,3,4,5]]\n", + "\n", + "--- Case E3: Axis = Rank (dY1 is null/empty range -> 1) ---\n", + "Shape of input tensor: (2, 3)\n", + "X=[[0,1,2], [3,4,5]]\n", + "Shape of output tensor: (6, 1)\n", + "Result = [[0], [1], [2], [3], [4], [5]]\n" + ] + } + ], + "source": [ + "## Empty and Edge Cases\n", + "\n", + "# Case E1: Empty Tensor (une dimension est à 0)\n", + "# Un tenseur de forme (2, 0, 4) contient 0 éléments au total.\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.zeros((2, 0, 4)).astype(np.int32)\n", + "axis = 0\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)] # Sera [2, 0]\n", + "print(\"--- Case E1: Empty Tensor (shape 2,0,4) ---\")\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)\n", + "\n", + "# Case E1: Empty Tensor (une dimension est à 0)\n", + "# Un tenseur de forme (2, 0, 4) contient 0 éléments au total.\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.zeros((2, 0, 4)).astype(np.int32)\n", + "axis = 1\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)] # Sera [2, 0]\n", + "print(\"--- Case E1: Empty Tensor (shape 2,0,4) ---\")\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)\n", + "\n", + "# Case E1: Empty Tensor (une dimension est à 0)\n", + "# Un tenseur de forme (2, 0, 4) contient 0 éléments au total.\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.zeros((2, 0, 4)).astype(np.int32)\n", + "axis = 2\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)] # Sera [2, 0]\n", + "print(\"--- Case E1: Empty Tensor (shape 2,0,4) ---\")\n", + "session = create_flatten_model(axis, [None,None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)\n", + "\n", + "# Case E2: Axis = 0 (dY0 devient 1 par produit vide)\n", + "# Le produit sur un intervalle vide est défini comme 1.\n", + "x = np.arange(6).reshape(2, 3).astype(np.int32)\n", + "axis = 0\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis]) # Produit d'une liste vide = 1\n", + "dim1 = np.prod(input_shape[axis:])\n", + "output_shape = [int(dim0), int(dim1)] # Sera [1, 6]\n", + "print(\"\\n--- Case E2: Axis 0 (dY0 is null/empty range -> 1) ---\")\n", + "session = create_flatten_model(axis, [None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)\n", + "\n", + "# Case E3: Axis = Rank (dY1 devient 1 par produit vide)\n", + "x = np.arange(6).reshape(2, 3).astype(np.int32)\n", + "axis = 2 # Égal au rang rX\n", + "input_shape = x.shape \n", + "dim0 = np.prod(input_shape[:axis])\n", + "dim1 = np.prod(input_shape[axis:]) # Produit d'une liste vide = 1\n", + "output_shape = [int(dim0), int(dim1)] # Sera [6, 1]\n", + "print(\"\\n--- Case E3: Axis = Rank (dY1 is null/empty range -> 1) ---\")\n", + "session = create_flatten_model(axis, [None,None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "8fc227d0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Case S1: Scalar Input (rank 0) ---\n", + "Shape of input tensor: ()\n", + "X=42\n", + "Shape of output tensor: (1, 1)\n", + "Result = [[42]]\n" + ] + } + ], + "source": [ + "## Scalar Input Case (Rank 0)\n", + "\n", + "# Case S1: Scalar (int32), axis=0\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array(42).astype(np.int32)\n", + "axis = 0 \n", + "\n", + "input_shape = x.shape # ()\n", + "# np.prod([]) retourne 1.0 (float), on convertit en int\n", + "dim0 = np.prod(input_shape[:axis]) \n", + "dim1 = np.prod(input_shape[axis:]) \n", + "output_shape = [int(dim0), int(dim1)] # [1, 1]\n", + "\n", + "print(\"--- Case S1: Scalar Input (rank 0) ---\")\n", + "\n", + "# CORRECTION : Respect de l'ordre (axis, input_rank, output_shape, dtype)\n", + "session = create_flatten_model(axis, [], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "f8647632", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "--- Case: Empty Vector (0 elements) ---\n", + "Shape of input tensor: (0,)\n", + "X=[]\n", + "Shape of output tensor: (1, 0)\n", + "Result = []\n" + ] + } + ], + "source": [ + "## Empty Tensor Case (Total elements = 0)\n", + "\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "# Forme (0,) : Un vecteur qui ne contient rien\n", + "x = np.array([], dtype=np.float32) \n", + "axis = 0\n", + "\n", + "input_shape = x.shape # (0,)\n", + "dim0 = np.prod(input_shape[:axis]) # Produit vide = 1\n", + "dim1 = np.prod(input_shape[axis:]) # Produit de [0] = 0\n", + "output_shape = [int(dim0), int(dim1)] # [1, 0]\n", + "\n", + "print(\"--- Case: Empty Vector (0 elements) ---\")\n", + "session = create_flatten_model(axis, [None], output_shape, onnx_type)\n", + "do_flatten(x, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.onnx b/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.onnx new file mode 100644 index 00000000..f5df7dc6 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/flatten/flatten.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/READEME.md b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/READEME.md new file mode 100644 index 00000000..4b6e2443 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/READEME.md @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_flatten.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_flatten.py you need to do +```bash +pytest test_flatten.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_flatten.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_flatten.py + +# Explanations of Flatten corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Flatten tests +For the flatten operator we just **checked both the corner and edge cases** for the **rank_input_tensor**, **shape_input_tensor**, **x_type** and **axis**. + +We consider **rank_input_tensor**, **shape_input_tensor**, **x_type** and **axis** as **lines** since they are independent and we checked the corner and edge cases for both of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/check_edges.py new file mode 100644 index 00000000..6ec2646a --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/check_edges.py @@ -0,0 +1,124 @@ +""" +This file checks edge cases in the generated data for the Clip operation in ONNX. +""" +import json +import numpy as np +import ml_dtypes + +""" +Flatten supported types, organized by ONNXRuntime_Provider +""" +flatten_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + check_rank_input = check_individual_variables( + "rank_input_tensor", data["rank_input_tensor"], + data["min_rank_input"], data["max_rank_input"]) + + check_shape_size_input = check_individual_variables( + "shape_input_tensor", + [size for shape in data["shape_input_tensor"] for size in shape], + data["min_dim_size_input"], data["max_dim_size_input"]) + + provider = data["ONNXRuntime_Provider"] + + check_x_type = check_type("x_type", data["x_type"], provider) + + check_axis = check_individual_variables( + "axis", data["axis"], -data["max_rank_input"], data["max_rank_input"]) + + return all([check_rank_input, check_shape_size_input, check_x_type, check_axis]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(flatten_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + + +print(check_edges()) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/test_flatten.py b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/test_flatten.py new file mode 100644 index 00000000..2ab97cbb --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/flatten/hypothesis/test_flatten.py @@ -0,0 +1,335 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Flatten operation in ONNX. +""" +import os + +import json +import numpy as np +import ml_dtypes + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + + +from onnx import helper + +import tensorflow as tf + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Inputs/attributes for Flatten operation +""" + +inputs_attributes = { + "min_rank_input": 1, #Adjust as needed + "max_rank_input": 10, #Adjust as needed + "min_dim_size_input": 1, #Adjust as needed + "max_dim_size_input": 5, #Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + + +""" +Flatten supported types, organized by ONNXRuntime_Provider +""" +flatten_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + +dtype_to_key = {v: k for k, v in flatten_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Store generated data +""" +generated_data = { + "rank_input_tensor": [], + "shape_input_tensor": [], + "x_type": [], + "axis": [] +} + +""" +Function to generate valid flatten arguments +""" + +@st.composite +@settings() +def valid_slice_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + + # X [C2] - Input/Output Types Consistency + all_valid_types = list(flatten_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + input_type = draw(st.sampled_from(all_valid_types)) + input_dtype = flatten_types.get(inputs_attributes["ONNXRuntime_Provider"])[input_type] + + if np.issubdtype(input_dtype, np.integer): + min_val = np.iinfo(input_dtype).min + max_val = np.iinfo(input_dtype).max + input_strategy = st.integers(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.floating): + min_val = np.finfo(input_dtype).min + max_val = np.finfo(input_dtype).max + input_strategy = st.floats(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.bool_): + input_strategy = st.booleans() + elif np.issubdtype(input_dtype, np.str_): + input_strategy = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + elif input_type == "BFLOAT16": + min_bfloat16 = float(ml_dtypes.finfo(flatten_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).min) + max_bfloat16 = float(ml_dtypes.finfo(flatten_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).max) + input_strategy = st.floats(min_value=min_bfloat16, max_value=max_bfloat16) + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + rank_input_tensor = draw(st.integers( + min_value=inputs_attributes["min_rank_input"], + max_value=inputs_attributes["max_rank_input"] + )) + + shape_input_tensor = [] + for _ in range(rank_input_tensor): + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_size_input"], + max_value=inputs_attributes["max_dim_size_input"] + )) + shape_input_tensor.append(dim_size) + + if input_type == "BFLOAT16": + temp_tensor = draw(hnp.arrays(dtype=np.float32, shape=shape_input_tensor, elements=input_strategy)) + tf_tensor = tf.cast(tf.constant(temp_tensor), tf.bfloat16) + x = tf_tensor.numpy() + else: + x = draw(hnp.arrays(dtype=input_dtype, shape=shape_input_tensor, elements=input_strategy)) + + #--------------------------------------------------- + # Attribute axis + #--------------------------------------------------- + + # axis [C1] -> X [C1], axis [C2] - Axis Range + axis = draw(st.integers( + min_value=-(rank_input_tensor), + max_value=rank_input_tensor + )) + + #--------------------------------------------------- + # Output y + #--------------------------------------------------- + + # Y [C1] + y_shape = [] + dy0 = np.prod(shape_input_tensor[:axis]) + dy1 = np.prod(shape_input_tensor[axis:]) + y_shape = [int(dy0), int(dy1)] + + return x, axis, y_shape + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_slice_args()) +def test_flatten(args): + x, axis, y_shape = args + generated_data["rank_input_tensor"].append(len(x.shape)) + generated_data["shape_input_tensor"].append(list(x.shape)) + x_type_key = dtype_to_key.get(x.dtype.type, str(x.dtype)) + generated_data["x_type"].append(x_type_key) + generated_data["axis"].append(axis) + y = run_onnx_flatten_test(x, axis, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + if axis < 0: + axis += len(x.shape) + check_constraints(y_shape, y, x, axis) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated by Hypothesis for Flatten operation tests", + "min_rank_input": inputs_attributes["min_rank_input"], + "max_rank_input": inputs_attributes["max_rank_input"], + "rank_input_tensor": generated_data["rank_input_tensor"], + "min_dim_size_input": inputs_attributes["min_dim_size_input"], + "max_dim_size_input": inputs_attributes["max_dim_size_input"], + "shape_input_tensor": generated_data["shape_input_tensor"], + "x_type": generated_data["x_type"], + "axis": generated_data["axis"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"] + } + + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def run_onnx_flatten_test(x, axis, y_shape, provider): + """ + Function that runs the ONNX Slice operation + """ + x_onnx = helper.make_tensor_value_info('x', helper.np_dtype_to_tensor_dtype(x.dtype), x.shape) + + # Y [C3] -> X [C2] - Input/Output Types Consistency + y_onnx = helper.make_tensor_value_info('y', helper.np_dtype_to_tensor_dtype(x.dtype), y_shape) + + node_def = helper.make_node( + 'Flatten', + inputs=['x'], + outputs=['y'], + axis=axis + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test_flatten', + [x_onnx], + [y_onnx], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + if str(x.dtype) == "bfloat16": + # Use ONNX Reference Implementation for bfloat16 + # BFLOAT16 is not supported by ONNX Runtime while using numpy + # An alternative is to use torch tensores and CUDAProvider + sess = onnx.reference.ReferenceEvaluator(onnx_model) + else: + # Use ONNX Runtime for other types + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'x': x})[0] + print("y shape:", y.shape) + print("y dtype:", y.dtype) + print("y:", y) + return y + +def check_constraints(y_shape, y, x, axis): + """ + Check constraints for generated data + """ + # X[C1] + assert axis <= len(x.shape) + # X[C2] + x_is_string = np.issubdtype(x.dtype, np.str_) or np.issubdtype(x.dtype, np.object_) + y_is_string = np.issubdtype(y.dtype, np.str_) or np.issubdtype(y.dtype, np.object_) + if x_is_string and y_is_string: + pass + else: + assert x.dtype == y.dtype + # axis[C1] -> X[C1] + # axis[C2] + assert -len(x.shape) <= axis <= len(x.shape) + # Y + assert (len (y_shape) == 2) + # Y[C1] + assert y_shape == list(y.shape) + # Y[C2] + assert check_coords_value(x, y, axis) + # Y[C3] -> X[C2] + + + +def calculate_y_coords(x_coords, x_shape, axis): + """ + Calculate the corresponding coordinates in Y given coordinates in X + """ + n = len(x_shape) + a = 0 + for z in range(0, axis): + prod = 1 + for k in range(z + 1, axis): + prod *= x_shape[k] + a += x_coords[z] * prod + + b = 0 + for z in range(axis, n): + prod = 1 + for k in range(z + 1, n): + prod *= x_shape[k] + b += x_coords[z] * prod + + return (a, b) + +def check_coords_value(x, y, axis): + """ + Check if there is a valid correspondence between input and output values + """ + result = [] + it = np.nditer(x, flags=['multi_index']) + for x_value in it: + coords = it.multi_index + y_coords = calculate_y_coords(coords, list(x.shape), axis) + y_value = y[y_coords] + result.append(x_value == y_value) + return all(result) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/gemm/README.md b/safety-related-profile/sonnx/ops/spec/tests/gemm/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu.onnx b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu.onnx new file mode 100644 index 00000000..65a22b7e Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha001.onnx b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha001.onnx new file mode 100644 index 00000000..65a22b7e Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha001.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha01.onnx b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha01.onnx new file mode 100644 index 00000000..b23b62f9 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alpha01.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphainf.onnx b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphainf.onnx new file mode 100644 index 00000000..ab25ccab Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphainf.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphanan.onnx b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphanan.onnx new file mode 100644 index 00000000..0b47fbad Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_alphanan.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_doc.ipynb new file mode 100644 index 00000000..61486334 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/leakyrelu/leakyrelu_doc.ipynb @@ -0,0 +1,302 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[ 1.00000000,-2.00000000], [-3.00000000, 4.00000000]]\n", + "Result: leakyrelu(X)=[[ 1.00000000,-0.02000000], [-0.03000000, 4.00000000]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: leakyrelu(X)=[[1.00000000], [1.00000000]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: leakyrelu(X)=[[0.00000000], [0.00000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: leakyrelu(X)=[[-0.01000000], [-0.01000000]]\n", + "X=[[inf], [inf]]\n", + "Result: leakyrelu(X)=[[inf], [inf]]\n", + "X=[[-inf], [-inf]]\n", + "Result: leakyrelu(X)=[[-inf], [-inf]]\n", + "X=[[nan], [nan]]\n", + "Result: leakyrelu(X)=[[nan], [nan]]\n", + "X=[[-0.00000000], [-0.00000000]]\n", + "Result: leakyrelu(X)=[[-0.00000000], [-0.00000000]]\n", + "\n", + "## Example 1\n", + "X=[[ 6.09999990,-9.50000000,35.70000076]]\n", + "Result: leakyrelu(X)=[[ 6.09999990,-0.94999999,35.70000076]]\n", + "\n", + "## Example 2\n", + "X=[[ inf, nan, -inf,-0.00000000, 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: leakyrelu(X)=[[ inf, nan, -inf,-0.00000000, 0.00000000, 1.00000000,-0.01000000]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan, -inf,-0.00000000, 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: leakyrelu(X)=[[ inf, nan, nan,-0.00000000, 0.00000000, 1.00000000, nan]]\n", + "\n", + "## Example 4\n", + "X=[[ inf, nan, -inf,-0.00000000, 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: leakyrelu(X)=[[ inf, nan, inf,-0.00000000, 0.00000000, 1.00000000, inf]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "leakyrelu_output_name = \"LeakyReluOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after leakyrelu operation)\n", + "leakyrelu_output = onnx.helper.make_tensor_value_info(leakyrelu_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define LeakyRelu node: Y = LeakyRelu(X, alpha)\n", + "alpha_value = 0.01 # Example alpha (must be set)\n", + "leakyrelu_node = onnx.helper.make_node(\n", + " \"LeakyRelu\",\n", + " inputs=[input_name],\n", + " outputs=[leakyrelu_output_name],\n", + " alpha=alpha_value\n", + ")\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[leakyrelu_node],\n", + " name=\"LeakyRelu\",\n", + " inputs=[input_tensor],\n", + " outputs=[leakyrelu_output]\n", + ")\n", + "\n", + "# Create the ONNX model (LeakyRelu is available in opset 14)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 14)])\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"leakyrelu.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"leakyrelu.onnx\")\n", + "\n", + "def do_leakyrelu(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: leakyrelu(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, -2.0],\n", + " [-3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_leakyrelu(x)\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, inf, -0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\"), -0.0]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_leakyrelu(x_np)\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1 from spec\n", + "# X = [6.1, -9.5, 35.7], alpha = 0.1\n", + "alpha_value = 0.1\n", + "# Recreate model with alpha = 0.1\n", + "leakyrelu_node = onnx.helper.make_node(\n", + " \"LeakyRelu\",\n", + " inputs=[input_name],\n", + " outputs=[leakyrelu_output_name],\n", + " alpha=alpha_value\n", + ")\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[leakyrelu_node],\n", + " name=\"LeakyRelu\",\n", + " inputs=[input_tensor],\n", + " outputs=[leakyrelu_output]\n", + ")\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 14)])\n", + "onnx.checker.check_model(model)\n", + "onnx.save(model, \"leakyrelu_alpha01.onnx\")\n", + "session = ort.InferenceSession(\"leakyrelu_alpha01.onnx\")\n", + "\n", + "x_example_1 = np.array([[6.1, -9.5, 35.7]], dtype=np.float32)\n", + "do_leakyrelu(x_example_1)\n", + "\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2 from spec\n", + "# X = [inf, NaN, -inf, -0.0, 0.0, 1.0, -1.0], alpha = 0.01\n", + "alpha_value = 0.01\n", + "leakyrelu_node = onnx.helper.make_node(\n", + " \"LeakyRelu\",\n", + " inputs=[input_name],\n", + " outputs=[leakyrelu_output_name],\n", + " alpha=alpha_value\n", + ")\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[leakyrelu_node],\n", + " name=\"LeakyRelu\",\n", + " inputs=[input_tensor],\n", + " outputs=[leakyrelu_output]\n", + ")\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 14)])\n", + "onnx.checker.check_model(model)\n", + "onnx.save(model, \"leakyrelu_alpha001.onnx\")\n", + "session = ort.InferenceSession(\"leakyrelu_alpha001.onnx\")\n", + "\n", + "x_example_2 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\"), -0.0, 0.0, 1.0, -1.0]\n", + "], dtype=np.float32)\n", + "do_leakyrelu(x_example_2)\n", + "\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3 from spec: alpha = NaN\n", + "alpha_value = float(\"nan\")\n", + "leakyrelu_node = onnx.helper.make_node(\n", + " \"LeakyRelu\",\n", + " inputs=[input_name],\n", + " outputs=[leakyrelu_output_name],\n", + " alpha=alpha_value\n", + ")\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[leakyrelu_node],\n", + " name=\"LeakyRelu\",\n", + " inputs=[input_tensor],\n", + " outputs=[leakyrelu_output]\n", + ")\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 14)])\n", + "onnx.checker.check_model(model)\n", + "onnx.save(model, \"leakyrelu_alphanan.onnx\")\n", + "session = ort.InferenceSession(\"leakyrelu_alphanan.onnx\")\n", + "\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\"), -0.0, 0.0, 1.0, -1.0]\n", + "], dtype=np.float32)\n", + "do_leakyrelu(x_example_3)\n", + "\n", + "\n", + "print(\"\\n## Example 4\")\n", + "\n", + "# Example 4 from spec: alpha = -inf\n", + "alpha_value = float(\"-inf\")\n", + "leakyrelu_node = onnx.helper.make_node(\n", + " \"LeakyRelu\",\n", + " inputs=[input_name],\n", + " outputs=[leakyrelu_output_name],\n", + " alpha=alpha_value\n", + ")\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[leakyrelu_node],\n", + " name=\"LeakyRelu\",\n", + " inputs=[input_tensor],\n", + " outputs=[leakyrelu_output]\n", + ")\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 14)])\n", + "onnx.checker.check_model(model)\n", + "onnx.save(model, \"leakyrelu_alphainf.onnx\")\n", + "session = ort.InferenceSession(\"leakyrelu_alphainf.onnx\")\n", + "\n", + "x_example_4 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\"), -0.0, 0.0, 1.0, -1.0]\n", + "], dtype=np.float32)\n", + "do_leakyrelu(x_example_4)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/less/README.md b/safety-related-profile/sonnx/ops/spec/tests/less/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/less/less_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/less/less_doc.ipynb new file mode 100644 index 00000000..ff5245cf --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/less/less_doc.ipynb @@ -0,0 +1,264 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[2.00000000,3.00000000,7.00000000]]\n", + "B=[[3.00000000,3.00000000,5.00000000]]\n", + "Result: Less(A,B)=[[ True,False,False]]\n", + "A=[[1.00000000,2.00000000], [4.00000000,0.00000000], [5.00000000,6.00000000]]\n", + "B=[[3.00000000,2.00000000], [4.00000000,1.00000000], [5.00000000,4.00000000]]\n", + "Result: Less(A,B)=[[ True,False], [False, True], [False,False]]\n", + "A=[[1.00000000], [1.00000000]]\n", + "B=[[1.00000000], [1.00000000]]\n", + "Result: Less(A,B)=[[False], [False]]\n", + "A=[[nan], [nan]]\n", + "B=[[0.00000000], [0.00000000]]\n", + "Result: Less(A,B)=[[False], [False]]\n", + "A=[[inf], [inf]]\n", + "B=[[inf], [inf]]\n", + "Result: Less(A,B)=[[False], [False]]\n", + "A=[[-inf], [-inf]]\n", + "B=[[-inf], [-inf]]\n", + "Result: Less(A,B)=[[False], [False]]\n", + "\n", + "## Example 1\n", + "A=[[2.50000000,3.70000005,7.90000010]]\n", + "B=[[3.09999990,3.00000000,5.80000019]]\n", + "Result: Less(A,B)=[[ True,False,False]]\n", + "\n", + "## Example 2\n", + "A=[[1.10000002,2.00000000], [4.19999981,0.00000000], [5.30000019,6.40000010]]\n", + "B=[[3.50000000,2.00000000], [4.59999990,1.00000000], [5.69999981,4.80000019]]\n", + "Result: Less(A,B)=[[ True,False], [ True, True], [ True,False]]\n", + "\n", + "## Example 3\n", + "A=[[ -inf, -inf, -inf, -inf, 0.00000000, 0.00000000, 0.00000000, 0.00000000, inf, inf, inf, inf, nan, nan, nan, nan,-0.00000000, 0.00000000]]\n", + "B=[[ -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, 0.00000000,-0.00000000]]\n", + "Result: Less(A,B)=[[False, True, True,False,False,False, True,False,False,False,False,False,False,False,False,False,False,False]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name_A = \"A\"\n", + "input_name_B = \"B\"\n", + "less_output_name = \"LessOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensors\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensors\n", + "input_tensor_A = onnx.helper.make_tensor_value_info(\n", + " input_name_A, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "input_tensor_B = onnx.helper.make_tensor_value_info(\n", + " input_name_B, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "# Create output tensor (result after Less operation)\n", + "less_output = onnx.helper.make_tensor_value_info(\n", + " less_output_name, onnx.TensorProto.BOOL, [None, None]\n", + ")\n", + "\n", + "# Define Less node: C = Less(A, B)\n", + "less_node = onnx.helper.make_node(\n", + " \"Less\",\n", + " [input_name_A, input_name_B],\n", + " [less_output_name]\n", + ")\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[less_node],\n", + " name=\"Less\",\n", + " inputs=[input_tensor_A, input_tensor_B],\n", + " outputs=[less_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Less is available in opset 13+; we use 16 here)\n", + "model = onnx.helper.make_model(\n", + " graph,\n", + " opset_imports=[onnx.helper.make_opsetid(\"\", 16)]\n", + ")\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"less.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"less.onnx\")\n", + "\n", + "def do_less(a, b):\n", + " # Run inference\n", + " output = session.run(None, {input_name_A: a, input_name_B: b})\n", + "\n", + " a_f = (np.array2string(a, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " b_f = (np.array2string(b, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"A={a_f}\")\n", + " print(f\"B={b_f}\")\n", + " print(f\"Result: Less(A,B)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensors (float32)\n", + "a = np.array([[2.0, 3.0, 7.0]], dtype=np.float32)\n", + "b = np.array([[3.0, 3.0, 5.0]], dtype=np.float32)\n", + "do_less(a, b)\n", + "\n", + "a = np.array([[1.0, 2.0],\n", + " [4.0, 0.0],\n", + " [5.0, 6.0]], dtype=np.float32)\n", + "\n", + "b = np.array([[3.0, 2.0],\n", + " [4.0, 1.0],\n", + " [5.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_less(a, b)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, +inf, -inf)\n", + "#--------------------------------------------------------------------\n", + "\n", + "vA = [1.0, float(\"nan\"), float(\"inf\"), float(\"-inf\")]\n", + "vB = [1.0, 0.0, float(\"inf\"), float(\"-inf\")]\n", + "\n", + "for a_val, b_val in zip(vA, vB):\n", + " a_np = np.array([[a_val],\n", + " [a_val]], dtype=np.float32)\n", + " b_np = np.array([[b_val],\n", + " [b_val]], dtype=np.float32)\n", + " do_less(a_np, b_np)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# A = [2.5, 3.7, 7.9]\n", + "# B = [3.1, 3.7, 5.8]\n", + "a_example_1 = np.array([[2.5, 3.7, 7.9]], dtype=np.float32)\n", + "b_example_1 = np.array([[3.1, 3.0, 5.8]], dtype=np.float32)\n", + "do_less(a_example_1, b_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# A =\n", + "# [[ 1.1 2.0 ]\n", + "# [ 4.2 0.0 ]\n", + "# [ 5.3 6.4 ]]\n", + "# B =\n", + "# [[ 3.5 2 ]\n", + "# [ 4.6 1.0 ]\n", + "# [ 5.3 4.8 ]]\n", + "a_example_2 = np.array([\n", + " [1.1, 2.0],\n", + " [4.2, 0.0],\n", + " [5.3, 6.4]\n", + "], dtype=np.float32)\n", + "\n", + "b_example_2 = np.array([\n", + " [3.5, 2.0],\n", + " [4.6, 1.0],\n", + " [5.7, 4.8]\n", + "], dtype=np.float32)\n", + "\n", + "do_less(a_example_2, b_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# A = [[ -inf -inf -inf -inf 0.0 0.0 0.0 0.0 inf inf inf inf nan nan nan nan ]]\n", + "# B = [[ -inf 0.0 inf nan -inf 0.0 inf nan -inf 0.0 inf nan -inf 0.0 inf nan ]]\n", + "a_example_3 = np.array([\n", + " [float(\"-inf\"), float(\"-inf\"), float(\"-inf\"), float(\"-inf\"), 0.0, 0.0, 0.0, 0.0, float(\"inf\"), float(\"inf\"), float(\"inf\"), float(\"inf\"), float(\"nan\"), float(\"nan\"), float(\"nan\"), float(\"nan\"), -0.0, 0.0]\n", + "], dtype=np.float32)\n", + "\n", + "b_example_3 = np.array([\n", + " [float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"), float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"), float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"), float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\") , 0.0 , -0.0 ]\n", + "], dtype=np.float32)\n", + "\n", + "do_less(a_example_3, b_example_3)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/log/README.md b/safety-related-profile/sonnx/ops/spec/tests/log/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/log/log_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/log/log_doc.ipynb new file mode 100644 index 00000000..f6fc1da2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/log/log_doc.ipynb @@ -0,0 +1,215 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "Result: log(X)=[[0.00000000,0.69314718], [1.09861231,1.38629436]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: log(X)=[[0.00000000], [0.00000000]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: log(X)=[[-inf], [-inf]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: log(X)=[[nan], [nan]]\n", + "X=[[inf], [inf]]\n", + "Result: log(X)=[[inf], [inf]]\n", + "X=[[-inf], [-inf]]\n", + "Result: log(X)=[[nan], [nan]]\n", + "X=[[nan], [nan]]\n", + "Result: log(X)=[[nan], [nan]]\n", + "\n", + "## Example 1\n", + "X=[[1.00000000,2.00000000,4.00000000]]\n", + "Result: log(X)=[[0.00000000,0.69314718,1.38629436]]\n", + "\n", + "## Example 2\n", + "X=[[ 2.71799994e+00,-7.38899994e+00], [ 0.00000000e+00, 1.00000001e-01], [ 1.00000000e+01,-1.00000000e+03]]\n", + "Result: log(X)=[[ 0.99989629, nan], [ -inf,-2.30258512], [ 2.30258512, nan]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan, -inf,-0.00000000, 0.00000000]]\n", + "Result: log(X)=[[ inf, nan, nan,-inf,-inf]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "log_output_name = \"LogOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name,onnx.TensorProto.FLOAT,[None, None])\n", + "\n", + "# Create output tensor (final result after log operation)\n", + "log_output = onnx.helper.make_tensor_value_info(log_output_name,onnx.TensorProto.FLOAT,[None, None])\n", + "\n", + "# Define Log node: Y = Log(X)\n", + "log_node = onnx.helper.make_node(\"Log\",[input_name],[log_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[log_node],\n", + " name=\"Log\",\n", + " inputs=[input_tensor],\n", + " outputs=[log_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Log is available in opset 13)\n", + "model = onnx.helper.make_model(graph,opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"log.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"log.onnx\")\n", + "\n", + "def do_log(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: log(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],\n", + " [3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_log(x)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_log(x_np)\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [1, 2, 4]\n", + "x_example_1 = np.array([[1.0, 2.0, 4.0]], dtype=np.float32)\n", + "do_log(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[ 2.718 -7.389 ]\n", + "# [ 0 0.1 ]\n", + "# [ 10 -1000 ]]\n", + "x_example_2 = np.array([\n", + " [2.718, -7.389],\n", + " [0.0, 0.1],\n", + " [10.0, -1000.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_log(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# X =\n", + "# [[ inf nan -inf -0.0 0.0]\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\"), -0.0, 0.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_log(x_example_3)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/lstm/README.md b/safety-related-profile/sonnx/ops/spec/tests/lstm/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/README.md b/safety-related-profile/sonnx/ops/spec/tests/matmul/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/README b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/README new file mode 100644 index 00000000..477f1250 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/README @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_matmul.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_matmul.py you need to do +```bash +pytest test_matmul.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_matmul.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_matmul.py + +# Explanations of Matmul corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Matmul tests +For the Matmul operator we just **checked both the corner and edge cases** for the **size_input_a** and **size_input_b**. + +We consider **size_input_a**, **size_input_b** as **lines** since they are independent and we checked the corner and edge cases for both of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/check_edges.py new file mode 100644 index 00000000..43b5c10c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/check_edges.py @@ -0,0 +1,61 @@ +""" +This file checks edge cases in the generated data for the Matmul operation in ONNX. +""" +import json +import numpy as np + +matmul_types = { + "INT32": np.int32, + "INT64": np.int64, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP32": np.float32, + "FP64": np.float64 +} + + +def check_edges(data): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + size_input_axis_a = data["size_input_a"] + size_input_axis_flatten_a = [item for sublist in size_input_axis_a for item in sublist] + check_size_input_axis_a = check_individual_variables( + "size_input_a", size_input_axis_flatten_a, + data["min_size_input_a"], data["max_size_input_a"]) + + size_input_axis_b = data["size_input_b"] + size_input_axis_flatten_b = [item for sublist in size_input_axis_b for item in sublist] + check_size_input_axis_b = check_individual_variables( + "size_input_b", size_input_axis_flatten_b, + data["min_size_input_b"], data["max_size_input_b"]) + + return all([check_size_input_axis_a, check_size_input_axis_b]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + +print(check_edges("generated_data.json")) diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/test_matmul.py b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/test_matmul.py new file mode 100644 index 00000000..11ef9998 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/matmul/hypothesis/test_matmul.py @@ -0,0 +1,268 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Matmul operation in ONNX. +""" +import os + +import json +import numpy as np + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes details +""" +inputs_attributes = { + "min_size_input_axis": 1, # If you generate UINT32/UINT64, you cant set this to 0. + "max_size_input_axis": 10, # Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + +""" +Matmul supported types +""" +matmul_types = { + "CPUExecutionProvider": { + "INT32": np.int32, + "INT64": np.int64, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP32": np.float32, + "FP64": np.float64 + }, + "CUDAExecutionProvider": { + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64 + }, + "DmlExecutionProvider": { + "FP16": np.float16, + "FP32": np.float32 + } +} + +""" +Store generated data +""" +generated_data = { + "size_input_a": [], + "size_input_b": [] +} + +""" +Function to generate valid matmul arguments +""" +@st.composite +def valid_matmul_args(draw): + + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + # Type Consistency + + all_valid_types = list(matmul_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + dtype_name = draw(st.sampled_from(all_valid_types)) + dtype = matmul_types[inputs_attributes["ONNXRuntime_Provider"]][dtype_name] + + if np.issubdtype(dtype, np.integer): + min_value = np.iinfo(dtype).min + max_value = np.iinfo(dtype).max + a_numeric = st.integers(min_value=min_value, max_value=max_value) + elif np.issubdtype(dtype, np.floating): + min_value = np.finfo(dtype).min + max_value = np.finfo(dtype).max + a_numeric = st.floats(min_value=min_value, max_value=max_value) + + #--------------------------------------------------- + # Input A + #--------------------------------------------------- + + # a_shape is the shape of input tensor A + # A [C1] + a_rank = 2 + a_shape = [] + + for _ in range(a_rank): + dim = draw(st.integers( + min_value=inputs_attributes["min_size_input_axis"], + max_value=inputs_attributes["max_size_input_axis"])) + a_shape.append(dim) + + # Create input tensor A + a = draw(hnp.arrays(dtype=dtype, shape=a_shape, elements=a_numeric)) + + #--------------------------------------------------- + # Input B + #--------------------------------------------------- + + # b_shape is the shape of input tensor B + # B [C1] + b_rank = 2 + b_shape = [0] * b_rank + + # B [C2] -> A [C2] + b_shape[0] = a_shape[1] + + dim = draw(st.integers( + min_value=inputs_attributes["min_size_input_axis"], + max_value=inputs_attributes["max_size_input_axis"])) + b_shape[1] = dim + + #Create input tensor B + b = draw(hnp.arrays(dtype=dtype, shape=b_shape, elements=a_numeric)) + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + + # y_shape is the shape of output tensor Y + # Y [C1] + # Y [C2] -> A [C2] + # Y [C2] -> B [C2] + y_shape = [a_shape[0], b_shape[1]] + + return (a_shape, b_shape, a, b, dtype_name, y_shape) + +""" +Function that runs the test +""" +@settings(max_examples=20000, deadline=None) +@given(valid_matmul_args()) +def test_matmul(args): + a_shape, b_shape, a, b, dtype_name, y_shape = args + generated_data["size_input_a"].append(a_shape) + generated_data["size_input_b"].append(b_shape) + + y = run_onnx_matmul(a_shape, b_shape, a, b, dtype_name, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + check_constraints(a, b, y) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated for testing ONNX Matmul Operator", + "min_size_input_a": inputs_attributes["min_size_input_axis"], + "max_size_input_a": inputs_attributes["max_size_input_axis"], + "size_input_a": generated_data["size_input_a"], + "min_size_input_b": inputs_attributes["min_size_input_axis"], + "max_size_input_b": inputs_attributes["max_size_input_axis"], + "size_input_b": generated_data["size_input_b"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + + +def run_onnx_matmul(a_shape, b_shape, a, b, dtype_name, y_shape, provider): + """ + Function that runs the ONNX Matmul operation + """ + + # Create inputs + a_onnx = helper.make_tensor_value_info('a', + helper.np_dtype_to_tensor_dtype(a.dtype), + list(a_shape)) + b_onnx = helper.make_tensor_value_info('b', + helper.np_dtype_to_tensor_dtype(b.dtype), + list(b_shape)) + + node_def = helper.make_node( + 'MatMul', + ['a', 'b'], + ['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-matmul', + [a_onnx, b_onnx], + [helper.make_tensor_value_info('y', + helper.np_dtype_to_tensor_dtype(a.dtype), + y_shape)], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 13 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'a': a, 'b': b})[0] + + for tensor in [a, b]: + print("a shape:", tensor.shape) + print("a values:", tensor) + + print("y shape:", y.shape) + print("y values:", y) + print("y data type:", y.dtype) + return y + +def matrix_multiplication (a,b): + """ + Function that performs matrix multiplication + """ + a_rows = a.shape[0] + b_cols = b.shape[1] + + y = np.zeros((a_rows, b_cols), dtype=a.dtype) + for i in range (a_rows): + for j in range(b_cols): + sum = 0 + for k in range(a.shape[1]): + sum += a[i][k] * b[k][j] + y[i][j] = sum + return y + +def check_constraints(a, b, y): + """ + Function that defines asserts for the constraints + """ + + # Inputs Constraints + # A [C1] + # B [C1] + # Y [C1] + assert len(a.shape) == len(b.shape) == len(y.shape) == 2 + + # A [C2] - Shape Consistency + # B [C2] -> A [C2] + assert a.shape[1] == b.shape[0] + + # Type consistency + assert a.dtype == b.dtype == y.dtype + + # A [C3] - Output Shape + # B [C3] + # Y [C3] -> A [C3], B [C3] + assert y.shape == (a.shape[0], b.shape[1]) + + # Output Result Check + y_expected = matrix_multiplication(a, b) + # Note: Due to rounding errors the entries are compared with a tolerance. + if not (np.isinf(y_expected).any() or np.isnan(y_expected).any()): + assert np.allclose(y, y_expected, rtol=1e-05, atol=1e-08, equal_nan=True) + diff --git a/safety-related-profile/sonnx/ops/spec/tests/matmul/matmul.ipynb b/safety-related-profile/sonnx/ops/spec/tests/matmul/matmul.ipynb new file mode 100644 index 00000000..e9a79d5b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/matmul/matmul.ipynb @@ -0,0 +1,366 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 3, + "id": "02a475ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.3.3)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"A\"\n", + "input2_name = \"B\"\n", + "matmul_output_name = \"Y\"\n", + "\n", + "\n", + "# Create the ONNX model with Matmul operator\n", + "def create_matmul_model(dtype, output_rank):\n", + "\n", + " #Create \"input-rank\" inputs tensors\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, [None,None])\n", + " input2 = onnx.helper.make_tensor_value_info(input2_name, dtype, [None,None])\n", + "\n", + " # Create output tensor (final result after matmul operation)\n", + " matmul_output = onnx.helper.make_tensor_value_info(matmul_output_name, dtype, output_rank)\n", + "\n", + " # Define matmul node\n", + " matmul_node = onnx.helper.make_node(\n", + " \"MatMul\",\n", + " inputs=[input1_name, input2_name],\n", + " outputs=[matmul_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [matmul_node],\n", + " \"MatMul\",\n", + " [input1, input2],\n", + " [matmul_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"matmul.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"matmul.onnx\")\n", + " return session\n", + "\n", + "def do_matmul(a, b, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: a, input2_name: b})\n", + "\n", + " a_f = (np.array2string(a, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " b_f = (np.array2string(b, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"A={a_f}, B={b_f}\")\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[1,2], [3,4]], B=[[5,6], [7,8]]\n", + "Result = [[19,22], [43,50]]\n" + ] + } + ], + "source": [ + "# Case N1: 2 simple matrices multiplication\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([[1, 2], [3, 4]], dtype=np.int32)\n", + "b = np.array([[5, 6], [7, 8]], dtype=np.int32)\n", + "session = create_matmul_model(onnx_type, [2,2])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6f034cb4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[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]], B=[[1,0,0,0,0], [0,1,0,0,0], [0,0,1,0,0], [0,0,0,1,0], [0,0,0,0,1]]\n", + "Result = [[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]]\n" + ] + } + ], + "source": [ + "# Case N2: 2 simple matrices multiplication\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([[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]], dtype=np.int32)\n", + "b = np.identity(5, dtype=np.int32)\n", + "session = create_matmul_model(onnx_type, [5,5])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8aa8f0db", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[1,2], [3,4], [5,6]], B=[[ 7, 8, 9], [10,11,12]]\n", + "Result = [[ 27, 30, 33], [ 61, 68, 75], [ 95,106,117]]\n" + ] + } + ], + "source": [ + "# Case N3: 2 simple matrices multiplication\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([[1,2], [3,4], [5,6]], dtype=np.int32)\n", + "b = np.array([[7,8,9], [10,11,12]], dtype=np.int32)\n", + "session = create_matmul_model(onnx_type, [3,3])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1e80cd08", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(3, 1)\n", + "(1, 3)\n", + "A=[[1], [2], [3]], B=[[4,5,6]]\n", + "Result = [[ 4, 5, 6], [ 8,10,12], [12,15,18]]\n" + ] + } + ], + "source": [ + "#Case N4: 2 simple matrices multiplication\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([[1], [2], [3]], dtype=np.int32)\n", + "b = np.array([[4, 5, 6]], dtype=np.int32)\n", + "session = create_matmul_model(onnx_type, [3,3])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "ef1ebed6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[4,5,6]], B=[[1], [2], [3]]\n", + "Result = [[32]]\n" + ] + } + ], + "source": [ + "#Case N5: 2 simple matrices multiplication\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([[4, 5, 6]], dtype=np.int32)\n", + "b = np.array([[1], [2], [3]], dtype=np.int32)\n", + "session = create_matmul_model(onnx_type, [1,1])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "markdown", + "id": "b109280f", + "metadata": {}, + "source": [ + "## No nominal cases (nan and inf values)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3d2c4787", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[ inf,-inf, nan], [ nan, inf,-inf]], B=[[1.00000000,2.00000000], [4.00000000,5.00000000], [7.00000000,8.00000000]]\n", + "Result = [[nan,nan], [nan,nan]]\n" + ] + } + ], + "source": [ + "#Case N1: Infs and Nans\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "a = np.array([[np.inf, -np.inf, np.nan], [np.nan, np.inf, -np.inf]], dtype=np.float32)\n", + "b = np.array([[1, 2], [4, 5], [7, 8]], dtype=np.float32)\n", + "session = create_matmul_model(onnx_type, [2,2])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1bf7f34e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[inf,inf], [nan,inf]], B=[[1.00000000,2.00000000,3.00000000,4.00000000], [4.00000000,5.00000000,6.00000000,7.00000000]]\n", + "Result = [[inf,inf,inf,inf], [nan,nan,nan,nan]]\n" + ] + } + ], + "source": [ + "#Case N2: Infs and Nans\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "a = np.array([[np.inf, np.inf], [np.nan, np.inf]], dtype=np.float32)\n", + "b = np.array([[1, 2, 3, 4], [4, 5, 6, 7]], dtype=np.float32)\n", + "session = create_matmul_model(onnx_type, [2,4])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "markdown", + "id": "cb656e35", + "metadata": {}, + "source": [ + "## Empty tensors" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "14b7d0b1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[], B=[]\n", + "Result = [[0]]\n" + ] + } + ], + "source": [ + "#Case N1: Empty tensors\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([], dtype=np.int32).reshape(1,0)\n", + "b = np.array([], dtype=np.int32).reshape(0,1)\n", + "session = create_matmul_model(onnx_type, [1,1])\n", + "do_matmul(a, b, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a5db09ae", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[], B=[]\n", + "Result = []\n" + ] + } + ], + "source": [ + "#Case N2: Empty tensors\n", + "onnx_type = onnx.TensorProto.INT32\n", + "a = np.array([], dtype=np.int32).reshape(0,1)\n", + "b = np.array([], dtype=np.int32).reshape(1,0)\n", + "session = create_matmul_model(onnx_type, [0,0])\n", + "do_matmul(a, b, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/max/README.md b/safety-related-profile/sonnx/ops/spec/tests/max/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/max/max_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/max/max_doc.ipynb new file mode 100644 index 00000000..2d129ece --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/max/max_doc.ipynb @@ -0,0 +1,257 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[2.00000000,3.00000000,7.00000000]]\n", + "B=[[3.00000000,3.00000000,5.00000000]]\n", + "Result: Max(A,B)=[[3.00000000,3.00000000,7.00000000]]\n", + "A=[[1.00000000,2.00000000], [4.00000000,0.00000000], [5.00000000,6.00000000]]\n", + "B=[[3.00000000,2.00000000], [4.00000000,1.00000000], [5.00000000,4.00000000]]\n", + "Result: Max(A,B)=[[3.00000000,2.00000000], [4.00000000,1.00000000], [5.00000000,6.00000000]]\n", + "A=[[1.00000000], [1.00000000]]\n", + "B=[[1.00000000], [1.00000000]]\n", + "Result: Max(A,B)=[[1.00000000], [1.00000000]]\n", + "A=[[nan], [nan]]\n", + "B=[[0.00000000], [0.00000000]]\n", + "Result: Max(A,B)=[[nan], [nan]]\n", + "A=[[inf], [inf]]\n", + "B=[[inf], [inf]]\n", + "Result: Max(A,B)=[[inf], [inf]]\n", + "A=[[-inf], [-inf]]\n", + "B=[[-inf], [-inf]]\n", + "Result: Max(A,B)=[[-inf], [-inf]]\n", + "\n", + "## Example 1\n", + "A=[[2.50000000,3.70000005,7.90000010]]\n", + "B=[[3.09999990,3.00000000,5.80000019]]\n", + "Result: Max(A,B)=[[3.09999990,3.70000005,7.90000010]]\n", + "\n", + "## Example 2\n", + "A=[[1.10000002,2.00000000], [4.19999981,0.00000000], [5.30000019,6.40000010]]\n", + "B=[[3.50000000,2.00000000], [4.59999990,1.00000000], [5.69999981,4.80000019]]\n", + "Result: Max(A,B)=[[3.50000000,2.00000000], [4.59999990,1.00000000], [5.69999981,6.40000010]]\n", + "\n", + "## Example 3\n", + "A=[[ -inf, -inf, -inf, -inf, 0.00000000, 0.00000000, 0.00000000, 0.00000000, inf, inf, inf, inf, nan, nan, nan, nan,-0.00000000, 0.00000000]]\n", + "B=[[ -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, -inf, 0.00000000, inf, nan, 0.00000000,-0.00000000]]\n", + "Result: Max(A,B)=[[ -inf, 0.00000000, inf, nan, 0.00000000, 0.00000000, inf, nan, inf, inf, inf, nan, nan, nan, nan, nan,-0.00000000, 0.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name_A = \"A\"\n", + "input_name_B = \"B\"\n", + "max_output_name = \"MaxOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensors\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensors\n", + "input_tensor_A = onnx.helper.make_tensor_value_info(\n", + " input_name_A, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "input_tensor_B = onnx.helper.make_tensor_value_info(\n", + " input_name_B, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "# Create output tensor (result after Max operation)\n", + "max_output = onnx.helper.make_tensor_value_info(\n", + " max_output_name, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "# Define Max node: C = Max(A, B)\n", + "max_node = onnx.helper.make_node(\n", + " \"Max\",\n", + " [input_name_A, input_name_B],\n", + " [max_output_name]\n", + ")\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[max_node],\n", + " name=\"Max\",\n", + " inputs=[input_tensor_A, input_tensor_B],\n", + " outputs=[max_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Max is available in opset 13+; we use 16 here)\n", + "model = onnx.helper.make_model(\n", + " graph,\n", + " opset_imports=[onnx.helper.make_opsetid(\"\", 16)]\n", + ")\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"max.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"max.onnx\")\n", + "\n", + "def do_max(a, b):\n", + " # Run inference\n", + " output = session.run(None, {input_name_A: a, input_name_B: b})\n", + "\n", + " a_f = (np.array2string(a, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " b_f = (np.array2string(b, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " o_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"A={a_f}\")\n", + " print(f\"B={b_f}\")\n", + " print(f\"Result: Max(A,B)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensors (float32)\n", + "a = np.array([[2.0, 3.0, 7.0]], dtype=np.float32)\n", + "b = np.array([[3.0, 3.0, 5.0]], dtype=np.float32)\n", + "do_max(a, b)\n", + "\n", + "a = np.array([[1.0, 2.0],\n", + " [4.0, 0.0],\n", + " [5.0, 6.0]], dtype=np.float32)\n", + "\n", + "b = np.array([[3.0, 2.0],\n", + " [4.0, 1.0],\n", + " [5.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_max(a, b)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, +inf, -inf)\n", + "#--------------------------------------------------------------------\n", + "\n", + "vA = [1.0, float(\"nan\"), float(\"inf\"), float(\"-inf\")]\n", + "vB = [1.0, 0.0, float(\"inf\"), float(\"-inf\")]\n", + "\n", + "for a_val, b_val in zip(vA, vB):\n", + " a_np = np.array([[a_val],\n", + " [a_val]], dtype=np.float32)\n", + " b_np = np.array([[b_val],\n", + " [b_val]], dtype=np.float32)\n", + " do_max(a_np, b_np)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "a_example_1 = np.array([[2.5, 3.7, 7.9]], dtype=np.float32)\n", + "b_example_1 = np.array([[3.1, 3.0, 5.8]], dtype=np.float32)\n", + "do_max(a_example_1, b_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "a_example_2 = np.array([\n", + " [1.1, 2.0],\n", + " [4.2, 0.0],\n", + " [5.3, 6.4]\n", + "], dtype=np.float32)\n", + "\n", + "b_example_2 = np.array([\n", + " [3.5, 2.0],\n", + " [4.6, 1.0],\n", + " [5.7, 4.8]\n", + "], dtype=np.float32)\n", + "\n", + "do_max(a_example_2, b_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "a_example_3 = np.array([\n", + " [float(\"-inf\"), float(\"-inf\"), float(\"-inf\"), float(\"-inf\"),\n", + " 0.0, 0.0, 0.0, 0.0,\n", + " float(\"inf\"), float(\"inf\"), float(\"inf\"), float(\"inf\"),\n", + " float(\"nan\"), float(\"nan\"), float(\"nan\"), float(\"nan\"),\n", + " -0.0, 0.0]\n", + "], dtype=np.float32)\n", + "\n", + "b_example_3 = np.array([\n", + " [float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"),\n", + " float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"),\n", + " float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"),\n", + " float(\"-inf\"), 0.0, float(\"inf\"), float(\"nan\"),\n", + " 0.0, -0.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_max(a_example_3, b_example_3)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/hypothesis/test_maxpool.py b/safety-related-profile/sonnx/ops/spec/tests/maxpool/hypothesis/test_maxpool.py new file mode 100644 index 00000000..0ca5dd11 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/hypothesis/test_maxpool.py @@ -0,0 +1,352 @@ +""" +Using hypothesis to generate automatic tests for MaxPool operator (SONNX) +""" +import math +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +from onnx import helper, TensorProto +import onnx.checker +import onnx.printer +from onnxruntime import InferenceSession + + +#AUTO_PAD_OPTIONS = ["NOTSET", "VALID", "SAME_UPPER", "SAME_LOWER"] +AUTO_PAD_OPTIONS = ["NOTSET"] + +""" +Function to generate valid inputs/atributes for Conv operator +""" +@st.composite +def valid_maxpool_args(draw): + # x and w dimensions + dx0 = draw(st.integers(min_value=1, max_value=1)) +# dx1 = draw(st.integers(min_value=256, max_value=256)) + dx1 = draw(st.integers(min_value=2, max_value=2)) +# dx2 = draw(st.integers(min_value=32, max_value=256)) + dx2 = draw(st.integers(min_value=8, max_value=8)) +# dx3 = draw(st.integers(min_value=32, max_value=256)) + dx3 = draw(st.integers(min_value=8, max_value=8)) +# dw0 = draw(st.integers(min_value=1, max_value=dx2)) +# dw1 = draw(st.integers(min_value=1, max_value=dx3)) + dw0 = draw(st.integers(min_value=3, max_value=3)) + dw1 = draw(st.integers(min_value=3, max_value=3)) + + + # x + min_value = -10000.0 + max_value = 10000.0 + a_numeric = st.floats(min_value=min_value, max_value=max_value) + x = draw(hnp.arrays(dtype=np.float32, shape=(dx0, dx1, dx2, dx3), elements=a_numeric)) + + + # Atributes +# pads = draw(st.lists( +# st.integers(min_value=0, max_value=1000), min_size=4, max_size=4) + +# )#FIXME: Check this Max Value +# pads_0 = draw(st.integers(min_value=0, max_value=(dw0-1))) +# pads_2 = draw(st.integers(min_value=0, max_value=(dw0-1))) +# pads_1 = draw(st.integers(min_value=0, max_value=(dw1-1))) +# pads_3 = draw(st.integers(min_value=0, max_value=(dw1-1))) + pads_0 = draw(st.integers(min_value=0, max_value=(1))) + pads_2 = draw(st.integers(min_value=0, max_value=(1))) + pads_1 = draw(st.integers(min_value=0, max_value=(1))) + pads_3 = draw(st.integers(min_value=0, max_value=(1))) + pads = [pads_0, pads_1, pads_2, pads_3] +# strides = draw(st.lists(st.integers(min_value=1, max_value=1000), min_size=2, max_size=2) + strides = draw(st.lists(st.integers(min_value=1, max_value=1), min_size=2, max_size=2) + ) #FIXME: Check this Max Value + auto_pad = draw(st.sampled_from(AUTO_PAD_OPTIONS)) + kernel_shape = [dw0, dw1] + + # Auxiliary variables + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + line_dilation_max = math.floor((myalpha-1) /(dw0 -1)) if dw0 > 1 else 1 + column_dilation_max = math.floor((mybeta-1) /(dw1 -1)) if dw1 > 1 else 1 + line_dilation_value = draw(st.integers(min_value=1, max_value=line_dilation_max)) + column_dilation_value = draw(st.integers(min_value=1, max_value=column_dilation_max)) + + # Atributes + dilation = [line_dilation_value, column_dilation_value] + + return x, pads, strides, dilation, auto_pad, kernel_shape + +""" +Run ONNX runtime with generated inputs/atributes and check constraints +""" +@settings(max_examples= 1000,deadline=None) +@given(valid_maxpool_args()) +def test_maxpool(args): + print("--------------------------------------------------") + x, pads, strides, dilation, auto_pad, kernel_shape = args + dx0, dx1, dx2, dx3 = x.shape + dw0, dw1 = kernel_shape + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw0 - 1)) + 1 + mygamma = (dilation[1] * (dw1 - 1)) + 1 +#dY_2 = \left\lfloor{(dX_2 + pad\_shape[0] - dilations[0] * (kernel\_shape[0] - 1) - 1) / strides[0] + 1}\right\rfloor +#Where: pad_shape[i] is the sum of the pads along spatial axis i +# mydy2 = math.floor((myalpha - (mytheta)) / strides[0])) + 1 + mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1) +#dY_3 = \left\lfloor{(dX_3 + pad\_shape[1] - dilations[1] * (kernel\_shape[1] - 1) - 1) / strides[1] + 1} \right\rfloor +# where: pad_shape[i] is the sum of the pads along spatial axis i +# mydy3 = math.floor((mybeta - (mygamma)) / strides[1]) + 1 + mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1) + +# x_onnx = helper.make_tensor_value_info('x_onnx', TensorProto.FLOAT, [dx0, dx1, dx2, dx3]) + x_onnx = helper.make_tensor_value_info('x_onnx', helper.np_dtype_to_tensor_dtype(x.dtype), [dx0, dx1, dx2, dx3]) + + + if auto_pad == "NOTSET": + node_def = helper.make_node( + 'MaxPool', + ['x_onnx'], + ['y_Onnx', 'indices_Onnx'], + dilations=dilation, + kernel_shape=kernel_shape, + pads=pads, + strides=strides, + auto_pad='NOTSET', + ) + else: + node_def = helper.make_node( + 'MaxPool', + ['x_onnx'], + ['y_Onnx', 'indices_Onnx'], + dilations=dilation, + kernel_shape=[dw0, dw1], + strides=strides, + auto_pad=auto_pad, + ) + + graph_def = helper.make_graph( + [node_def], + 'test-maxpool', + [x_onnx], + [helper.make_tensor_value_info('y_Onnx', TensorProto.FLOAT, [dx0, dx1, mydy2, mydy3]), + helper.make_tensor_value_info('indices_Onnx', TensorProto.INT64, [dx0, dx1, mydy2, mydy3])], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 8 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession(onnx_model.SerializeToString(), providers=["CPUExecutionProvider"]) + +#2026-01-06 09:44:30.219581227 [E:onnxruntime:, inference_session.cc:2280 operator()] Exception during initialization: +# /onnxruntime_src/onnxruntime/core/providers/cpu/nn/pool_attributes.h:78 +# onnxruntime::PoolAttributes::PoolAttributes(const onnxruntime::OpNodeProtoHelper&, +# const std::string&, int) pads[dim] < kernel_shape[dim] && pads[dim + kernel_shape.size()] < kernel_shape[dim] was false. +# Pad should be smaller than kernel. + + # Initialize tensors + x = x.reshape(dx0, dx1, dx2, dx3).astype(np.float32) + + print("Pads", pads) + print("Strides", strides) + print("Dilation", dilation) + print("Auto_pad", auto_pad) + y = sess.run(None, {'x_onnx': x})[0] + indices = sess.run(None, {'x_onnx': x})[1] + + print("x shape:", x.shape) + print("x:", x) + +# padded_x = pad_4d_spatial_js(x, pads) +# print ("padded_x:", padded_x) + + print("w shape:", kernel_shape) + #print("w:", w) + + print("mydy2:", mydy2) + print("mydy3:", mydy3) + print("Y shape:", y.shape) + print("Indices shape:", indices.shape) + print("Y:", y) + + check_constraints(x, auto_pad, y, mydy2, mydy3, kernel_shape, strides, node_def, pads, dilation) + +def check_constraints(x, auto_pad, y, mydy2, + mydy3, kernel_shape, strides, + node_def, pads, dilation): + + #x - Constraints + # C1 + assert x.ndim == 4 and x.shape[2] >= 0 and x.shape[3] >= 0 + # C2: see C2 of y + + assert all(dim > 0 for dim in x.shape) + + + #w - Constraints + # TBD + assert len(kernel_shape) == 2 + assert all(dim > 0 for dim in kernel_shape) + + #Strides - Constraints + # C1 + assert all(s > 0 for s in strides) + # C2 + assert len(strides) == 2 + + + #Auto_pad - Constraints + #C1 + assert auto_pad in AUTO_PAD_OPTIONS + + #Pads - Constraints + # C1 + assert all(p >= 0 for p in pads) + # C2 + assert len(pads) == (x.ndim - 2) * 2 + # C3 + # Same of C3 of x + + + #Dilation - Constraints + # C1 + assert all(d > 0 for d in dilation) + # C2 + assert len(dilation) == len(kernel_shape) + # C3 + # Same of C3 of x + + + #kernel shape - Constraints + # C1 + for kernel_value in kernel_shape: + assert kernel_value > 0 + # C2 + + + #Y - Constraints + # C1 + #Same of C3 of x + + #Shape of the outpout + my_y_shape = compute_MaxPool_output_shape(x.shape, kernel_shape, pads, dilation, strides) + print("check_constraints: my_y_shape =", my_y_shape) + assert np.array_equal(y.shape, my_y_shape) + + #Functional check + my_y = MaxPool(x, kernel_shape, strides, dilation, pads) + print("check_constraints: my_y =", my_y) + assert np.array_equal(y, my_y) + +def compute_MaxPool_output_shape(input_shape, kernel_shape, pads, dilation, strides): + + dx0, dx1, dx2, dx3 = input_shape + dw0, dw1 = kernel_shape + + + myalpha = dx2 + pads[0] + pads[2] + mybeta = dx3 + pads[1] + pads[3] + mytheta = (dilation[0] * (dw0 - 1)) + 1 + mygamma = (dilation[1] * (dw1 - 1)) + 1 +# dY_2 = \left\lfloor{(dX_2 + pad\_shape[0] - dilations[0] * (kernel\_shape[0] - 1) - 1) / (strides[0] + 1)}\right\rfloor + mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1) +# dY_3 = \left\lfloor{(dX_3 + pad\_shape[1] - dilations[1] * (kernel\_shape[1] - 1) - 1) / (strides[1] + 1)} \right\rfloor + mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1) + + output_shape = [dx0, dx1, mydy2, mydy3] + + return output_shape + + +def pad_4d_spatial_js(tensor, paddings): + """ + Pads the last two dimensions (H, W) of a 4D tensor. + paddings = (top, left, bottom, right) + """ + N, C, H, W = tensor.shape + top, left, bottom, right = paddings + + # 1. Calculate the new spatial dimensions + new_H = H + top + bottom + new_W = W + left + right + + # 2. Create the new 4D "canvas" of zeros + # The Batch (N) and Channel (C) dimensions remain the same +# padded_tensor = np.zeros((N, C, new_H, new_W), dtype=tensor.dtype) + padded_tensor = np.full((N, C, new_H, new_W), -np.inf) + + # 3. Insert the original tensor into the padded one + # We use ":" for N and C to select all batches and channels + # We use slicing for the H and W dimensions + padded_tensor[:, :, top : top + H, left : left + W] = tensor + + return padded_tensor + +def extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilations, dW): + """ + Extracts the maximum value from a dilated window. + + Args: + X_p: The 2D padded input array. + m, n: Integer constants representing the current output position. + strides: Tuple (s_h, s_w) for step size. + dilations: Tuple (d_h, d_w) for spacing between window elements. + dW: Tuple (dW0, dW1) for the window height and width. + """ + dW0, dW1 = dW + s_h, s_w = strides + d_h, d_w = dilations + + # Calculate the base (top-left) coordinates based on the stride + base_h = m * s_h + base_w = n * s_w + + # Initialize max_val with the first element of the window (h=0, w=0) + max_val = X_p[b, c, base_h, base_w] + + # Iterate through the window defined by dW0 and dW1 + for h in range(dW0): + for w in range(dW1): + # Calculate the current dilated coordinates + curr_h = base_h + h * d_h + curr_w = base_w + w * d_w + + # Extract the value at these coordinates + val = X_p[b, c, curr_h, curr_w] + + # Update the maximum found so far + if val > max_val: + max_val = val + + return max_val + +def MaxPool(X, kernel_shape, strides, dilation, pads): + + Y_dims = compute_MaxPool_output_shape(X.shape, kernel_shape, pads, dilation, strides) + + Y = np.zeros(Y_dims) + + X_p = pad_4d_spatial_js(X, pads) + dX_p0, dX_p1, dX_p2, dX_p3 = X_p.shape + dY_p0, dY_p1, dY_p2, dY_p3 = Y.shape + + print("MaxPool: X_p =", X_p) + + for b in range(dX_p0): + for c in range(dX_p1): + for m in range(dY_p2): + for n in range(dY_p3): + Y[b, c, m, n] = extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilation, kernel_shape) + + return Y \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_divergence.ipynb b/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_divergence.ipynb new file mode 100644 index 00000000..5bd1f3a3 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_divergence.ipynb @@ -0,0 +1,1573 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "7e420862", + "metadata": {}, + "source": [ + "This IS a Test of the installation" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "db948602", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2026-02-16 18:16:45.992852: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n", + "2026-02-16 18:16:46.031872: I tensorflow/core/platform/cpu_feature_guard.cc:210] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n", + "To enable the following instructions: AVX2 AVX512F AVX512_VNNI FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n", + "2026-02-16 18:16:46.875208: I tensorflow/core/util/port.cc:153] oneDNN custom operations are on. You may see slightly different numerical results due to floating-point round-off errors from different computation orders. To turn them off, set the environment variable `TF_ENABLE_ONEDNN_OPTS=0`.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Python: 3.12.12\n", + "ONNX Version: 1.17.0\n", + "\n", + "--- PyTorch ---\n", + "Version: 2.9.1+cu128\n", + "CUDA disponible: True\n", + "GPU détecté: NVIDIA GeForce RTX 3080 Laptop GPU\n", + "Test multiplication matrice PyTorch OK, somme: -201616.28125\n", + "\n", + "--- TensorFlow ---\n", + "Version: 2.20.0\n", + "GPU TensorFlow détecté: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]\n", + "Test multiplication matrice TF OK, somme: 115546.414\n", + "\n", + "--- ONNX Runtime ---\n", + "Version: 1.23.2\n", + "Providers disponibles: ['TensorrtExecutionProvider', 'CUDAExecutionProvider', 'CPUExecutionProvider']\n", + "\n", + "--- Test ONNX 'Where' operator ---\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING: All log messages before absl::InitializeLog() is called are written to STDERR\n", + "I0000 00:00:1771262207.284949 22516 gpu_device.cc:2020] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 13888 MB memory: -> device: 0, name: NVIDIA GeForce RTX 3080 Laptop GPU, pci bus id: 0000:01:00.0, compute capability: 8.6\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Provider TensorrtExecutionProvider: ✅ Test Where OK, sortie: [ 1. 20. 3.]\n", + "Provider CUDAExecutionProvider: ✅ Test Where OK, sortie: [ 1. 20. 3.]\n", + "Provider CPUExecutionProvider: ✅ Test Where OK, sortie: [ 1. 20. 3.]\n", + "\n", + "--- Comparaison NumPy ---\n", + "Résultat NumPy: [ 1. 20. 3.]\n" + ] + } + ], + "source": [ + "import sys, torch, tensorflow as tf, onnxruntime as ort, numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "\n", + "# --- Versions Python & ONNX ---\n", + "print(\"Python:\", sys.version.split()[0])\n", + "print(\"ONNX Version:\", onnx.__version__)\n", + "\n", + "# --- PyTorch ---\n", + "print(\"\\n--- PyTorch ---\")\n", + "print(\"Version:\", torch.__version__)\n", + "print(\"CUDA disponible:\", torch.cuda.is_available())\n", + "if torch.cuda.is_available():\n", + " print(\"GPU détecté:\", torch.cuda.get_device_name(0))\n", + " x = torch.randn(4096, 4096, device=\"cuda\")\n", + " y = x @ x\n", + " print(\"Test multiplication matrice PyTorch OK, somme:\", y.sum().item())\n", + "\n", + "# --- TensorFlow ---\n", + "print(\"\\n--- TensorFlow ---\")\n", + "print(\"Version:\", tf.__version__)\n", + "gpus = tf.config.list_physical_devices('GPU')\n", + "print(\"GPU TensorFlow détecté:\", gpus)\n", + "if gpus:\n", + " a = tf.random.normal([4096,4096])\n", + " b = tf.random.normal([4096,4096])\n", + " c = tf.matmul(a,b)\n", + " print(\"Test multiplication matrice TF OK, somme:\", tf.reduce_sum(c).numpy())\n", + "\n", + "# --- ONNX Runtime ---\n", + "print(\"\\n--- ONNX Runtime ---\")\n", + "print(\"Version:\", ort.__version__)\n", + "print(\"Providers disponibles:\", ort.get_available_providers())\n", + "\n", + "# --- Test ONNX 'Where' operator ---\n", + "print(\"\\n--- Test ONNX 'Where' operator ---\")\n", + "C = helper.make_tensor_value_info(\"C\", TensorProto.BOOL, [3])\n", + "X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [3])\n", + "Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [3])\n", + "Z = helper.make_tensor_value_info(\"Z\", TensorProto.FLOAT, [3])\n", + "node = helper.make_node(\"Where\", [\"C\",\"X\",\"Y\"], [\"Z\"])\n", + "graph = helper.make_graph([node], \"where_graph\", [C,X,Y], [Z])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 23)])\n", + "save(model, \"where_test.onnx\")\n", + "\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"where_test.onnx\", providers=[provider])\n", + " cond = np.array([True, False, True], dtype=bool)\n", + " x = np.array([1.0,2.0,3.0], dtype=np.float32)\n", + " y = np.array([10.0,20.0,30.0], dtype=np.float32)\n", + " z = sess.run(None, {\"C\":cond, \"X\":x, \"Y\":y})[0]\n", + " print(f\"Provider {provider}: ✅ Test Where OK, sortie:\", z)\n", + " except Exception as e:\n", + " print(f\"Provider {provider}: ❌ Erreur:\", e)\n", + "\n", + "# --- Comparaison NumPy ---\n", + "print(\"\\n--- Comparaison NumPy ---\")\n", + "cond = np.array([True, False, True])\n", + "x = np.array([1.0,2.0,3.0])\n", + "y = np.array([10.0,20.0,30.0])\n", + "z_np = np.where(cond,x,y)\n", + "print(\"Résultat NumPy:\", z_np)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ee0fb84d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' operator Nominal ---\n", + "\n", + "--- Rien de special a voir tout est OK, avec CPU/CUDA meme resultat ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0 6]\n", + " [8 8]]]]\n", + "Indices =\n", + " [[[[-4 5]\n", + " [ 7 7]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0 6]\n", + " [8 8]]]]\n", + "Indices =\n", + " [[[[-4 5]\n", + " [ 7 7]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0 6]\n", + " [8 8]]]]\n", + "Indices =\n", + " [[[[-4 5]\n", + " [ 7 7]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-02-16 18:16:48.009336902 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.013163081 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 0\n", + " ints: 0\n", + " ints: 0\n", + " ints: 0\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"dilations\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.013171022 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.013175856 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.013226412 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnxruntime as ort\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "\n", + "# --- Test ONNX 'MaxPool' operator Nominal ---\n", + "print(\"\\n--- Test ONNX 'MaxPool' operator Nominal ---\")\n", + "print(\"\\n--- Rien de special a voir tout est OK, avec CPU/CUDA meme resultat ---\")\n", + "\n", + "onnx_type = TensorProto.UINT8\n", + "\n", + "# Input / Output shapes\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 3, 3])\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 2, 2])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 2, 2])\n", + "\n", + "# MaxPool node (avec 2 sorties)\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[0, 0, 0, 0],\n", + " strides=[1, 1],\n", + " dilations=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_3x3\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 22)])\n", + "save(model, \"maxpool_test.onnx\")\n", + "\n", + "# Données demandées\n", + "data = [[[[ 0, 0, 5],\n", + " [ 0, 0, 6],\n", + " [ 7, 8, 0]]]]\n", + "\n", + "x = np.array(data, dtype=np.uint8)\n", + "\n", + "# Test pour tous les providers\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_test.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "907bbaa5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' operator (DOUBLE + padding + indices) ---\n", + "\n", + "--- voir le -4 (quand padding), avec CPU/CUDA meme resultat ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[ 0.00000000e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.72844928e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.83691720e+000 3.54234851e+000 3.54234851e+000 2.55354471e+000]\n", + " [ 2.83691720e+000 3.46789489e+000 3.46789489e+000 -1.79769313e+308]]]]\n", + "Indices =\n", + " [[[[ 0 1 1 2]\n", + " [ 3 1 1 2]\n", + " [ 6 4 4 5]\n", + " [ 6 7 7 -4]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[ 0.00000000e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.72844928e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.83691720e+000 3.54234851e+000 3.54234851e+000 2.55354471e+000]\n", + " [ 2.83691720e+000 3.46789489e+000 3.46789489e+000 -1.79769313e+308]]]]\n", + "Indices =\n", + " [[[[ 0 1 1 2]\n", + " [ 3 1 1 2]\n", + " [ 6 4 4 5]\n", + " [ 6 7 7 -4]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[ 0.00000000e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.72844928e+000 9.57875561e+000 9.57875561e+000 4.56432533e+000]\n", + " [ 2.83691720e+000 3.54234851e+000 3.54234851e+000 2.55354471e+000]\n", + " [ 2.83691720e+000 3.46789489e+000 3.46789489e+000 -1.79769313e+308]]]]\n", + "Indices =\n", + " [[[[ 0 1 1 2]\n", + " [ 3 1 1 2]\n", + " [ 6 4 4 5]\n", + " [ 6 7 7 -4]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-02-16 18:16:48.038449443 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.038494919 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"dilations\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.038499565 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.038503576 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.038534669 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnxruntime as ort\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "\n", + "# --- Test ONNX 'MaxPool' operator (DOUBLE + padding + indices) ---\n", + "print(\"\\n--- Test ONNX 'MaxPool' operator (DOUBLE + padding + indices) ---\")\n", + "print(\"\\n--- voir le -4 (quand padding), avec CPU/CUDA meme resultat ---\")\n", + "\n", + "onnx_type = TensorProto.DOUBLE\n", + "\n", + "# Input / Output shapes\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 3, 3])\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 4, 4])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 4, 4])\n", + "\n", + "# MaxPool node\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1],\n", + " dilations=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_double_pad\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 22)])\n", + "save(model, \"maxpool_test.onnx\")\n", + "\n", + "# Données demandées\n", + "data = [[[[0.0, 9.57875561, 4.56432533],\n", + " [2.72844928, 3.54234851, 2.55354471],\n", + " [2.83691720, 3.46789489, -np.inf]]]]\n", + "\n", + "x = np.array(data, dtype=np.float64)\n", + "\n", + "# Test pour tous les providers\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_test.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a965461e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' operator pour obtenir indice -3 ---\n", + "\n", + "--- voir le -4 (quand padding), avec CPU/CUDA mais divergence cette fois ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[ 0.0000000e+00 0.0000000e+00 -3.4028235e+38]\n", + " [ 1.0000000e+00 1.1000000e+00 1.1000000e+00]\n", + " [ 1.0000000e+00 1.1000000e+00 1.1000000e+00]]]]\n", + "Indices =\n", + " [[[[ 0 0 -3]\n", + " [ 2 3 3]\n", + " [ 2 3 3]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0. 0. 0. ]\n", + " [1. 1.1 1.1]\n", + " [1. 1.1 1.1]]]]\n", + "Indices =\n", + " [[[[ 0 0 -4]\n", + " [ 2 3 3]\n", + " [ 2 3 3]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[ 0.0000000e+00 0.0000000e+00 -3.4028235e+38]\n", + " [ 1.0000000e+00 1.1000000e+00 1.1000000e+00]\n", + " [ 1.0000000e+00 1.1000000e+00 1.1000000e+00]]]]\n", + "Indices =\n", + " [[[[ 0 0 -3]\n", + " [ 2 3 3]\n", + " [ 2 3 3]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-02-16 18:16:48.064136547 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.064180677 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"dilations\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.064186077 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.064190755 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.064227662 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "\n", + "print(\"\\n--- Test ONNX 'MaxPool' operator pour obtenir indice -3 ---\")\n", + "print(\"\\n--- voir le -4 (quand padding), avec CPU/CUDA mais divergence cette fois ---\")\n", + "\n", + "onnx_type = TensorProto.FLOAT\n", + "\n", + "# Input / Output shapes\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 3, 3])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 3])\n", + "\n", + "# MaxPool node (2x2 kernel, stride 1, padding 1)\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1],\n", + " dilations=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_neg3\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_neg3.onnx\")\n", + "\n", + "# Données d'entrée (2x2)\n", + "data = [[[[0.0, -np.inf], # placer -inf dans un coin pour forcer un indice négatif\n", + " [1.0, 1.1]]]]\n", + "\n", + "x = np.array(data, dtype=np.float32)\n", + "\n", + "# Test pour tous les providers disponibles\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_neg3.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7202e2b8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' operator ---\n", + "\n", + "--- voir le -5 (quand padding), avec CPU/CUDA mais divergence cette fois ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 1.0000000e-01 2.0000000e-01 3.0000001e-01\n", + " 3.0000001e-01]\n", + " [ 1.0000000e+00 1.1000000e+00 1.2000000e+00 1.3000000e+00\n", + " 1.3000000e+00]\n", + " [ 2.0000000e+00 2.0999999e+00 2.2000000e+00 2.3000000e+00\n", + " 2.3000000e+00]\n", + " [ 2.0000000e+00 2.0999999e+00 2.2000000e+00 2.3000000e+00\n", + " 2.3000000e+00]]]]\n", + "Indices =\n", + " [[[[-5 1 2 3 3]\n", + " [ 4 5 6 7 7]\n", + " [ 8 9 10 11 11]\n", + " [ 8 9 10 11 11]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0. 0.1 0.2 0.3 0.3]\n", + " [1. 1.1 1.2 1.3 1.3]\n", + " [2. 2.1 2.2 2.3 2.3]\n", + " [2. 2.1 2.2 2.3 2.3]]]]\n", + "Indices =\n", + " [[[[-6 1 2 3 3]\n", + " [ 4 5 6 7 7]\n", + " [ 8 9 10 11 11]\n", + " [ 8 9 10 11 11]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 1.0000000e-01 2.0000000e-01 3.0000001e-01\n", + " 3.0000001e-01]\n", + " [ 1.0000000e+00 1.1000000e+00 1.2000000e+00 1.3000000e+00\n", + " 1.3000000e+00]\n", + " [ 2.0000000e+00 2.0999999e+00 2.2000000e+00 2.3000000e+00\n", + " 2.3000000e+00]\n", + " [ 2.0000000e+00 2.0999999e+00 2.2000000e+00 2.3000000e+00\n", + " 2.3000000e+00]]]]\n", + "Indices =\n", + " [[[[-5 1 2 3 3]\n", + " [ 4 5 6 7 7]\n", + " [ 8 9 10 11 11]\n", + " [ 8 9 10 11 11]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-16 18:16:48.091550721 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.091570620 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.093533211 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.093567572 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.093572157 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.093576515 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.093614710 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.095508613 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.095522494 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.105830092 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.105844133 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "\n", + "print(\"\\n--- Test ONNX 'MaxPool' operator ---\")\n", + "print(\"\\n--- voir le -5 (quand padding), avec CPU/CUDA mais divergence cette fois ---\")\n", + "\n", + "onnx_type = TensorProto.FLOAT\n", + "\n", + "# Input / Output shapes\n", + "# On met une dimension H=3, W=4 pour matcher les données\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 3, 4])\n", + "# On met des dimensions dynamiques pour laisser ONNX calculer la sortie\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 3, 4])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 4])\n", + "\n", + "# MaxPool node (2x2 kernel, stride 1, padding 1)\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_corrige\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_corrige.onnx\")\n", + "\n", + "# Données d'entrée (1,1,3,4)\n", + "data = [[[[ -np.inf, 0.1, 0.2, 0.3],\n", + " [ 1.0, 1.1, 1.2, 1.3],\n", + " [ 2.0, 2.1, 2.2, 2.3]]]]\n", + "\n", + "x = np.array(data, dtype=np.float32)\n", + "\n", + "# Test pour tous les providers disponibles\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_corrige.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "a0529907", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' triple divergence indice et valeur ---\n", + "\n", + "--- voir le -3/-4 et le 3/-4 (quand padding), avec CPU/CUDA double divergence cette fois ---\n", + "\n", + "--- voir valeur de Y qui diverge aussi inf vs 0 ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 -3.4028235e+38 -3.4028235e+38]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]]]]\n", + "Indices =\n", + " [[[[-3 -3 -3]\n", + " [-3 3 3]\n", + " [-3 3 3]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0. 0. 0. ]\n", + " [0. 0. 0. ]\n", + " [0. 0. 1.1]]]]\n", + "Indices =\n", + " [[[[-4 -4 -4]\n", + " [-4 -4 -4]\n", + " [-4 -4 3]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 -3.4028235e+38 -3.4028235e+38]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]]]]\n", + "Indices =\n", + " [[[[-3 -3 -3]\n", + " [-3 3 3]\n", + " [-3 3 3]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-02-16 18:16:48.122995266 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.123044734 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"dilations\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.123049885 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.123054832 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.123090908 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "\n", + "print(\"\\n--- Test ONNX 'MaxPool' triple divergence indice et valeur ---\")\n", + "print(\"\\n--- voir le -3/-4 et le 3/-4 (quand padding), avec CPU/CUDA double divergence cette fois ---\")\n", + "print(\"\\n--- voir valeur de Y qui diverge aussi inf vs 0 ---\")\n", + "\n", + "onnx_type = TensorProto.FLOAT\n", + "\n", + "# Input / Output shapes\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 3, 3])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 3])\n", + "\n", + "# MaxPool node (2x2 kernel, stride 1, padding 1)\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1],\n", + " dilations=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_neg3\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_neg3.onnx\")\n", + "\n", + "# Données d'entrée (2x2)\n", + "data = [[[[np.nan, np.nan], # placer -inf dans un coin pour forcer un indice négatif\n", + " [np.nan, 1.1]]]]\n", + "\n", + "x = np.array(data, dtype=np.float32)\n", + "\n", + "# Test pour tous les providers disponibles\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_neg3.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "f048be66", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "--- Test ONNX 'MaxPool' operator ---\n", + "\n", + "--- voir le valeur et indice avec CPU/CUDA double divergence autre exemple ---\n", + "\n", + "Provider TensorrtExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 -3.4028235e+38 -3.4028235e+38 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 0.0000000e+00 0.0000000e+00 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 0.0000000e+00 0.0000000e+00 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 -3.4028235e+38 -3.4028235e+38 -3.4028235e+38\n", + " -3.4028235e+38]]]]\n", + "Indices =\n", + " [[[[-5 -5 -5 -5 -5]\n", + " [-5 5 5 -5 -5]\n", + " [-5 5 5 -5 -5]\n", + " [-5 -5 -5 -5 -5]]]]\n", + "\n", + "Provider CUDAExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0.]\n", + " [0. 0. 0. 0. 0.]]]]\n", + "Indices =\n", + " [[[[-6 -6 -6 -6 -6]\n", + " [-6 -6 -6 -6 -6]\n", + " [-6 -6 5 -6 -6]\n", + " [-6 -6 -6 -6 -6]]]]\n", + "\n", + "Provider CPUExecutionProvider: ✅ Test MaxPool OK\n", + "Sortie Y =\n", + " [[[[-3.4028235e+38 -3.4028235e+38 -3.4028235e+38 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 0.0000000e+00 0.0000000e+00 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 0.0000000e+00 0.0000000e+00 -3.4028235e+38\n", + " -3.4028235e+38]\n", + " [-3.4028235e+38 -3.4028235e+38 -3.4028235e+38 -3.4028235e+38\n", + " -3.4028235e+38]]]]\n", + "Indices =\n", + " [[[[-5 -5 -5 -5 -5]\n", + " [-5 5 5 -5 -5]\n", + " [-5 5 5 -5 -5]\n", + " [-5 -5 -5 -5 -5]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-16 18:16:48.152591937 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.152612660 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.154548714 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:135: While parsing node number 0 [MaxPool -> \"Y\"]:\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.154584237 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:138: --- Begin node ---\n", + "input: \"X\"\n", + "output: \"Y\"\n", + "output: \"Indices\"\n", + "name: \"\"\n", + "op_type: \"MaxPool\"\n", + "attribute {\n", + " name: \"storage_order\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"strides\"\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"pads\"\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " ints: 1\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"kernel_shape\"\n", + " ints: 2\n", + " ints: 2\n", + " type: INTS\n", + "}\n", + "attribute {\n", + " name: \"ceil_mode\"\n", + " i: 0\n", + " type: INT\n", + "}\n", + "attribute {\n", + " name: \"auto_pad\"\n", + " s: \"NOTSET\"\n", + " type: STRING\n", + "}\n", + "\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.154588658 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:139: --- End node ---\u001b[m\n", + "\u001b[1;31m2026-02-16 18:16:48.154592959 [E:onnxruntime:Default, tensorrt_execution_provider.h:90 log] [2026-02-16 17:16:48 ERROR] ModelImporter.cpp:141: ERROR: onnxOpCheckers.cpp:151 In function emptyOutputChecker:\n", + "[8] This version of TensorRT doesn't support mode than 1 outputs for MaxPool nodes!\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.154628826 [W:onnxruntime:Default, tensorrt_execution_provider.cc:2833 GetCapability] [TensorRT EP] No graph will run on TensorRT execution provider\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.156042996 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.156055304 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.162276833 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-16 18:16:48.162290060 [W:onnxruntime:, graph.cc:120 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,4,5} target:{1,1,3,4}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "\n", + "print(\"\\n--- Test ONNX 'MaxPool' operator ---\")\n", + "print(\"\\n--- voir le valeur et indice avec CPU/CUDA double divergence autre exemple ---\")\n", + "\n", + "onnx_type = TensorProto.FLOAT\n", + "\n", + "# Input / Output shapes\n", + "# On met une dimension H=3, W=4 pour matcher les données\n", + "X = helper.make_tensor_value_info(\"X\", onnx_type, [1, 1, 3, 4])\n", + "# On met des dimensions dynamiques pour laisser ONNX calculer la sortie\n", + "Y = helper.make_tensor_value_info(\"Y\", onnx_type, [1, 1, 3, 4])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 4])\n", + "\n", + "# MaxPool node (2x2 kernel, stride 1, padding 1)\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1],\n", + " ceil_mode=0\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_graph_corrige\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_corrige.onnx\")\n", + "\n", + "# Données d'entrée (1,1,3,4)\n", + "data = [[[[ np.nan, np.nan, np.nan, np.nan],\n", + " [ np.nan, 0.0, np.nan, np.nan],\n", + " [ np.nan, np.nan, np.nan, np.nan]]]]\n", + "\n", + "x = np.array(data, dtype=np.float32)\n", + "\n", + "# Test pour tous les providers disponibles\n", + "for provider in ort.get_available_providers():\n", + " try:\n", + " sess = ort.InferenceSession(\"maxpool_corrige.onnx\", providers=[provider])\n", + "\n", + " outputs = sess.run(None, {\"X\": x})\n", + " y = outputs[0]\n", + " indices = outputs[1]\n", + "\n", + " print(f\"\\nProvider {provider}: ✅ Test MaxPool OK\")\n", + " print(\"Sortie Y =\\n\", y)\n", + " print(\"Indices =\\n\", indices)\n", + "\n", + " except Exception as e:\n", + " print(f\"\\nProvider {provider}: ❌ Erreur:\", e)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "22c2dd5f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Test minimal divergence NaN / Inf sur MaxPool ===\n", + "\n", + "--- comparer les resultats quand on appel l'operateur avec indice et sans indice, ca influence les valeur de sortie ---\n", + "\n", + "--- voir ce test et celui d'apres ---\n", + "\n", + "Entrée X =\n", + "[[[[nan inf]\n", + " [-1. 1.]]]]\n", + "\n", + "Provider CPUExecutionProvider\n", + "Sortie Y =\n", + "[[[[nan inf inf]\n", + " [nan inf inf]\n", + " [-1. 1. 1.]]]]\n", + "\n", + "Provider CUDAExecutionProvider\n", + "Sortie Y =\n", + "[[[[nan nan inf]\n", + " [nan nan inf]\n", + " [-1. 1. 1.]]]]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "import os\n", + "\n", + "print(\"\\n=== Test minimal divergence NaN / Inf sur MaxPool ===\")\n", + "print(\"\\n--- comparer les resultats quand on appel l'operateur avec indice et sans indice, ca influence les valeur de sortie ---\")\n", + "print(\"\\n--- voir ce test et celui d'apres ---\")\n", + "\n", + "\n", + "# ==========================================================\n", + "# 1️⃣ Modèle MaxPool simple\n", + "# ==========================================================\n", + "\n", + "X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [1, 1, 3, 3])\n", + "\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1]\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_nan_inf\", [X], [Y])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_nan_inf.onnx\")\n", + "\n", + "# ==========================================================\n", + "# 2️⃣ Input ciblé : NaN + +Inf\n", + "# ==========================================================\n", + "\n", + "x = np.array(\n", + " [[[[np.nan, np.inf],\n", + " [-1.0, 1.0]]]],\n", + " dtype=np.float32\n", + ")\n", + "\n", + "print(\"\\nEntrée X =\")\n", + "print(x)\n", + "\n", + "# ==========================================================\n", + "# 3️⃣ Exécution CPU vs CUDA\n", + "# ==========================================================\n", + "\n", + "results = {}\n", + "\n", + "for provider in [\"CPUExecutionProvider\", \"CUDAExecutionProvider\"]:\n", + " if provider not in ort.get_available_providers():\n", + " continue\n", + "\n", + " sess = ort.InferenceSession(\"maxpool_nan_inf.onnx\", providers=[provider])\n", + " y = sess.run(None, {\"X\": x})[0]\n", + " results[provider] = y\n", + "\n", + " print(f\"\\nProvider {provider}\")\n", + " print(\"Sortie Y =\")\n", + " print(y)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "73e8b4fb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Test minimal divergence NaN / Inf sur MaxPool (avec indices) ===\n", + "\n", + "--- comparer les resultats quand on appel l'operateur avec indice et sans indice, ca influence les valeur de sortie ---\n", + "\n", + "--- voir ce test et celui d'avant des nan et des inf deviennent egale a 0---\n", + "\n", + "Entrée X =\n", + "[[[[nan inf]\n", + " [-1. 1.]]]]\n", + "\n", + "Provider CPUExecutionProvider\n", + "Sortie Y =\n", + "[[[[-3.4028235e+38 inf inf]\n", + " [-1.0000000e+00 inf inf]\n", + " [-1.0000000e+00 1.0000000e+00 1.0000000e+00]]]]\n", + "Indices =\n", + "[[[[-3 1 1]\n", + " [ 2 1 1]\n", + " [ 2 3 3]]]]\n", + "\n", + "Provider CUDAExecutionProvider\n", + "Sortie Y =\n", + "[[[[ 0. 0. 0.]\n", + " [ 0. 0. 0.]\n", + " [-1. 1. 1.]]]]\n", + "Indices =\n", + "[[[[-4 -4 -4]\n", + " [-4 -4 -4]\n", + " [ 2 3 3]]]]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "import os\n", + "\n", + "print(\"\\n=== Test minimal divergence NaN / Inf sur MaxPool (avec indices) ===\")\n", + "print(\"\\n--- comparer les resultats quand on appel l'operateur avec indice et sans indice, ca influence les valeur de sortie ---\")\n", + "print(\"\\n--- voir ce test et celui d'avant des nan et des inf deviennent egale a 0---\")\n", + "\n", + "# ==========================================================\n", + "# 1️⃣ Modèle MaxPool simple (retourne Y et Indices)\n", + "# ==========================================================\n", + "\n", + "X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [1, 1, 3, 3])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 3])\n", + "\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1]\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_nan_inf\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_nan_inf.onnx\")\n", + "\n", + "# ==========================================================\n", + "# 2️⃣ Input ciblé : NaN + +Inf\n", + "# ==========================================================\n", + "\n", + "x = np.array(\n", + " [[[[np.nan, np.inf],\n", + " [-1.0, 1.0]]]],\n", + " dtype=np.float32\n", + ")\n", + "\n", + "print(\"\\nEntrée X =\")\n", + "print(x)\n", + "\n", + "# ==========================================================\n", + "# 3️⃣ Exécution CPU vs CUDA\n", + "# ==========================================================\n", + "\n", + "results = {}\n", + "\n", + "for provider in [\"CPUExecutionProvider\", \"CUDAExecutionProvider\"]:\n", + " if provider not in ort.get_available_providers():\n", + " continue\n", + "\n", + " sess = ort.InferenceSession(\"maxpool_nan_inf.onnx\", providers=[provider])\n", + " y, indices = sess.run(None, {\"X\": x})\n", + " results[provider] = (y, indices)\n", + "\n", + " print(f\"\\nProvider {provider}\")\n", + " print(\"Sortie Y =\")\n", + " print(y)\n", + " print(\"Indices =\")\n", + " print(indices)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "4ef890da", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Test minimal divergence NaN / Inf sur MaxPool ===\n", + "\n", + "Entrée X =\n", + "[[[[nan nan]\n", + " [nan 1.1]]]]\n", + "\n", + "Provider CPUExecutionProvider\n", + "Sortie Y =\n", + "[[[[ nan nan -3.4028235e+38]\n", + " [ nan nan -3.4028235e+38]\n", + " [ nan 1.1000000e+00 1.1000000e+00]]]]\n", + "\n", + "Provider CUDAExecutionProvider\n", + "Sortie Y =\n", + "[[[[nan nan nan]\n", + " [nan nan nan]\n", + " [nan nan 1.1]]]]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "import os\n", + "\n", + "print(\"\\n=== Test minimal divergence NaN / Inf sur MaxPool ===\")\n", + "\n", + "# ==========================================================\n", + "# 1️⃣ Modèle MaxPool simple\n", + "# ==========================================================\n", + "\n", + "X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [1, 1, 3, 3])\n", + "\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1]\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_nan_inf\", [X], [Y])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_nan_inf.onnx\")\n", + "\n", + "# ==========================================================\n", + "# 2️⃣ Input ciblé : NaN + +Inf\n", + "# ==========================================================\n", + "\n", + "x = np.array(\n", + " [[[[np.nan, np.nan],\n", + " [np.nan, 1.1]]]],\n", + " dtype=np.float32\n", + ")\n", + "\n", + "print(\"\\nEntrée X =\")\n", + "print(x)\n", + "\n", + "# ==========================================================\n", + "# 3️⃣ Exécution CPU vs CUDA\n", + "# ==========================================================\n", + "\n", + "results = {}\n", + "\n", + "for provider in [\"CPUExecutionProvider\", \"CUDAExecutionProvider\"]:\n", + " if provider not in ort.get_available_providers():\n", + " continue\n", + "\n", + " sess = ort.InferenceSession(\"maxpool_nan_inf.onnx\", providers=[provider])\n", + " y = sess.run(None, {\"X\": x})[0]\n", + " results[provider] = y\n", + "\n", + " print(f\"\\nProvider {provider}\")\n", + " print(\"Sortie Y =\")\n", + " print(y)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "eba4f257", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "=== Test minimal divergence NaN / Inf sur MaxPool (avec indices) ===\n", + "\n", + "Entrée X =\n", + "[[[[nan nan]\n", + " [nan 1.1]]]]\n", + "\n", + "Provider CPUExecutionProvider\n", + "Sortie Y =\n", + "[[[[-3.4028235e+38 -3.4028235e+38 -3.4028235e+38]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]\n", + " [-3.4028235e+38 1.1000000e+00 1.1000000e+00]]]]\n", + "Indices =\n", + "[[[[-3 -3 -3]\n", + " [-3 3 3]\n", + " [-3 3 3]]]]\n", + "\n", + "Provider CUDAExecutionProvider\n", + "Sortie Y =\n", + "[[[[0. 0. 0. ]\n", + " [0. 0. 0. ]\n", + " [0. 0. 1.1]]]]\n", + "Indices =\n", + "[[[[-4 -4 -4]\n", + " [-4 -4 -4]\n", + " [-4 -4 3]]]]\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "import onnx\n", + "from onnx import helper, TensorProto, save\n", + "import onnxruntime as ort\n", + "import os\n", + "\n", + "print(\"\\n=== Test minimal divergence NaN / Inf sur MaxPool (avec indices) ===\")\n", + "\n", + "# ==========================================================\n", + "# 1️⃣ Modèle MaxPool simple (retourne Y et Indices)\n", + "# ==========================================================\n", + "\n", + "X = helper.make_tensor_value_info(\"X\", TensorProto.FLOAT, [1, 1, 2, 2])\n", + "Y = helper.make_tensor_value_info(\"Y\", TensorProto.FLOAT, [1, 1, 3, 3])\n", + "Indices = helper.make_tensor_value_info(\"Indices\", TensorProto.INT64, [1, 1, 3, 3])\n", + "\n", + "node = helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[\"X\"],\n", + " outputs=[\"Y\", \"Indices\"],\n", + " kernel_shape=[2, 2],\n", + " pads=[1, 1, 1, 1],\n", + " strides=[1, 1]\n", + ")\n", + "\n", + "graph = helper.make_graph([node], \"maxpool_nan_inf\", [X], [Y, Indices])\n", + "model = helper.make_model(graph, opset_imports=[helper.make_opsetid(\"\", 13)])\n", + "save(model, \"maxpool_nan_inf.onnx\")\n", + "\n", + "# ==========================================================\n", + "# 2️⃣ Input ciblé : NaN + +Inf\n", + "# ==========================================================\n", + "\n", + "x = np.array(\n", + " [[[[np.nan, np.nan],\n", + " [np.nan, 1.1]]]],\n", + " dtype=np.float32\n", + ")\n", + "\n", + "print(\"\\nEntrée X =\")\n", + "print(x)\n", + "\n", + "# ==========================================================\n", + "# 3️⃣ Exécution CPU vs CUDA\n", + "# ==========================================================\n", + "\n", + "results = {}\n", + "\n", + "for provider in [\"CPUExecutionProvider\", \"CUDAExecutionProvider\"]:\n", + " if provider not in ort.get_available_providers():\n", + " continue\n", + "\n", + " sess = ort.InferenceSession(\"maxpool_nan_inf.onnx\", providers=[provider])\n", + " y, indices = sess.run(None, {\"X\": x})\n", + " results[provider] = (y, indices)\n", + "\n", + " print(f\"\\nProvider {provider}\")\n", + " print(\"Sortie Y =\")\n", + " print(y)\n", + " print(\"Indices =\")\n", + " print(indices)\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_doc.ipynb new file mode 100644 index 00000000..21bcebe2 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/maxpool_doc.ipynb @@ -0,0 +1,345 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "U5A9c4B_s72W" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (6.33.0)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.9.23)\n", + "Requirement already satisfied: packaging in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fc6VkO6dfbuD", + "outputId": "67515005-b301-4a66-ba77-413564e2194f" + }, + "outputs": [], + "source": [ + "import math\n", + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "from onnx import TensorProto\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "maxpool_output_name1 = \"Y\"\n", + "maxpool_output_name2 = \"Indices\"\n", + "\n", + "\n", + "# Create the ONNX model with maxpool operator\n", + "def create_maxpool_model(input_shape, output_shape, dtype, dilation, paddings, k_shape, store_order=0):\n", + "\n", + " #Create \"input-rank\" input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_shape)\n", + "\n", + " # Create output tensor (final result after maxpool operation)\n", + " maxpool_output1 = onnx.helper.make_tensor_value_info(maxpool_output_name1, dtype, output_shape)\n", + " maxpool_output2 = onnx.helper.make_tensor_value_info(maxpool_output_name2, TensorProto.INT64, output_shape)\n", + "\n", + " # Define MaxPool node\n", + " maxpool_node = onnx.helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[input1_name],\n", + " outputs=[maxpool_output_name1, maxpool_output_name2],\n", + " ceil_mode = 0,\n", + " dilations=dilation,\n", + " kernel_shape=k_shape,\n", + " pads=paddings,\n", + " strides=[1, 1],\n", + " auto_pad='NOTSET',\n", + " storage_order=store_order\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [maxpool_node],\n", + " \"Test_MaxPool\",\n", + " [input1],\n", + " [maxpool_output1, maxpool_output2],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)]) # Explicitly set opset to 22\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"maxpool.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"maxpool.onnx\")\n", + " return session\n", + "\n", + "def do_maxpool(x, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " indices_f = (np.array2string(output[1], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + "\n", + " # Display results\n", + " print(f\"X = \\n{x_f}\")\n", + " print(f\"Y = \\n{y_f}\")\n", + " print(f\"Indices = \\n{indices_f}\")\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 2.14485954, 3.60685315, 5.43428613],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "Y = \n", + "[[[[3.60685315,5.43428613],\n", + " [4.82017601,4.82017601]]]]\n", + "Indices = \n", + "[[[[1,2],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "# Case noninal N1\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[2.14485954, 3.60685315, 5.43428613],\n", + " [0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.60685315, 3.60685315, 5.43428613],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "Y = \n", + "[[[[3.60685315,5.43428613],\n", + " [4.82017601,4.82017601]]]]\n", + "Indices = \n", + "[[[[0,2],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "# Case noninal N2: max in at least two positions in X\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[3.60685315, 3.60685315, 5.43428613],\n", + " [0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.60685315, 3.60685315, 3.60685315],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "Y = \n", + "[[[[3.60685315,3.60685315],\n", + " [4.82017601,4.82017601]]]]\n", + "Indices = \n", + "[[[[0,1],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "# Case noninal N3: max in at least two positions in X\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[3.60685315, 3.60685315, 3.60685315],\n", + " [0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.60685315, 3.60685315, 3.60685315],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 4.82017601]]]]\n", + "Y = \n", + "[[[[3.60685315,3.60685315],\n", + " [4.82017601,4.82017601]]]]\n", + "Indices = \n", + "[[[[0,1],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "# Case noninal N3: max in at least two positions in X\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[3.60685315, 3.60685315, 3.60685315],\n", + " [0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 4.82017601]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[3.60685315,3.60685315,3.60685315],\n", + " [3.60685315,3.60685315,3.60685315],\n", + " [3.60685315,3.60685315,3.60685315]]]]\n", + "Y = \n", + "[[[[3.60685315,3.60685315],\n", + " [3.60685315,3.60685315]]]]\n", + "Indices = \n", + "[[[[0,1],\n", + " [3,4]]]]\n" + ] + } + ], + "source": [ + "# Case noninal N4: max in at least two positions in X\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[3.60685315, 3.60685315, 3.60685315],\n", + " [3.60685315, 3.60685315, 3.60685315],\n", + " [3.60685315, 3.60685315, 3.60685315]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOKOYLaMLVHJNovPEUX24CO", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_onnx.ipynb b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_onnx.ipynb new file mode 100644 index 00000000..137230d0 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_onnx.ipynb @@ -0,0 +1,231 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "U5A9c4B_s72W" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (1.13.0)\n", + "Requirement already satisfied: onnxruntime in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (1.13.1)\n", + "Requirement already satisfied: numpy>=1.16.6 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (1.24.1)\n", + "Requirement already satisfied: protobuf<4,>=3.20.2 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (3.20.3)\n", + "Requirement already satisfied: typing-extensions>=3.6.2.1 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnx) (4.13.2)\n", + "Requirement already satisfied: coloredlogs in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (25.9.23)\n", + "Requirement already satisfied: packaging in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (22.0)\n", + "Requirement already satisfied: sympy in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from onnxruntime) (1.11.1)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath>=0.19 in /home/souyris/NWOW/IA/VirtualEnv/torch/lib/python3.8/site-packages (from sympy->onnxruntime) (1.2.1)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "from onnx import TensorProto\n", + "from onnx.helper import (\n", + " make_model, make_node, make_graph,\n", + " make_tensor_value_info)\n", + "from onnx.checker import check_model" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fc6VkO6dfbuD", + "outputId": "67515005-b301-4a66-ba77-413564e2194f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "graph test-maxpool (\n", + " %x[DOUBLE, 1x1x8x8]\n", + ") {\n", + " %y, %Indices = MaxPool[auto_pad = 'NOTSET', ceil_mode = 0, dilations = [1, 1], kernel_shape = [3, 3], pads = [1, 0, 1, 0], strides = [1, 1]](%x)\n", + " return %y, %Indices\n", + "}\n", + "X shape: (1, 1, 8, 8)\n", + "X: [[[[ 4.43305943 1.05817534 4.28962538 4.28470842 3.00644965\n", + " 5.69287057 5.72557783 1.31154885]\n", + " [ 5.43695168 -1.82703117 2.87699581 2.69635184 6.18242239\n", + " 7.48620983 1.11871504 1.60847976]\n", + " [ 6.1525279 3.05861691 0.24934671 2.01243241 5.21568223\n", + " 6.78521868 -0.2558449 7.63857434]\n", + " [ 1.04082415 -0.36311271 2.57890517 1.90215758 2.56666234\n", + " 2.53389661 6.59819875 4.78165774]\n", + " [ 2.40125121 3.4556453 2.94774791 4.78412922 3.21581614\n", + " -1.627449 1.59955765 4.47469832]\n", + " [ 4.72100973 2.43828795 3.51116899 -2.88811044 6.53731096\n", + " 4.34773169 4.63030148 3.9411301 ]\n", + " [ 3.00536547 6.61801766 3.65044952 1.56946725 4.29760772\n", + " 2.49783759 0.60523926 2.5508386 ]\n", + " [ 3.20546648 5.9064298 1.176297 2.52805038 6.64426788\n", + " 4.02089479 -0.4197793 5.60318406]]]]\n", + "Y shape: (1, 1, 8, 6)\n", + "Y: [[[[5.43695168 4.28962538 6.18242239 7.48620983 7.48620983 7.48620983]\n", + " [6.1525279 4.28962538 6.18242239 7.48620983 7.48620983 7.63857434]\n", + " [6.1525279 3.05861691 6.18242239 7.48620983 7.48620983 7.63857434]\n", + " [6.1525279 4.78412922 5.21568223 6.78521868 6.78521868 7.63857434]\n", + " [4.72100973 4.78412922 6.53731096 6.53731096 6.59819875 6.59819875]\n", + " [6.61801766 6.61801766 6.53731096 6.53731096 6.53731096 4.63030148]\n", + " [6.61801766 6.61801766 6.64426788 6.64426788 6.64426788 5.60318406]\n", + " [6.61801766 6.61801766 6.64426788 6.64426788 6.64426788 5.60318406]]]]\n", + "Indices shape: (1, 1, 8, 6)\n", + "Indices: [[[[ 8 2 12 13 13 13]\n", + " [16 2 12 13 13 23]\n", + " [16 17 12 13 13 23]\n", + " [16 35 20 21 21 23]\n", + " [40 35 44 44 30 30]\n", + " [49 49 44 44 44 46]\n", + " [49 49 60 60 60 63]\n", + " [49 49 60 60 60 63]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/var/tmp/ipykernel_134949/1289850978.py:45: DeprecationWarning: Deprecated since 1.19. Consider using onnx.printer.to_text() instead.\n", + " print(onnx.helper.printable_graph(onnx_model.graph))\n" + ] + } + ], + "source": [ + "# @title Standard max pooling (1 channel)\n", + "import numpy as np\n", + "from onnx import *\n", + "import onnxruntime\n", + "\n", + "# Create inputs\n", + "x = helper.make_tensor_value_info('x', TensorProto.DOUBLE, [1, 1, 8, 8])\n", + "\n", + "# Create a node (MaxPool) with input/outputs\n", + "node_def = make_node(\n", + " 'MaxPool', # node name\n", + " ['x'], # inputs\n", + " ['y', 'Indices'], # outputs\n", + " ceil_mode = 0,\n", + " dilations=[1,1],\n", + " kernel_shape=[3,3],\n", + " pads=[1, 0, 1, 0],\n", + " strides=[1, 1],\n", + " auto_pad='NOTSET',\n", + "# group=1, # Standard convolution\n", + ")\n", + "\n", + "# Create the graph\n", + "graph_def = make_graph(\n", + " [node_def],\n", + " 'test-maxpool',\n", + " [x],\n", + " [helper.make_tensor_value_info('y', TensorProto.DOUBLE, [1, 1, 8, 6]), \n", + " helper.make_tensor_value_info('Indices', TensorProto.INT64, [1, 1, 8, 6])],\n", + ")\n", + "\n", + "onnx_model = make_model(graph_def)\n", + "\n", + "# Let's freeze the opset.\n", + "del onnx_model.opset_import[:]\n", + "opset = onnx_model.opset_import.add()\n", + "opset.domain = ''\n", + "opset.version = 15\n", + "onnx_model.ir_version = 8\n", + "\n", + "# Verify the model\n", + "check_model(onnx_model)\n", + "\n", + "# Print a human-readable representation of the graph\n", + "print(onnx.helper.printable_graph(onnx_model.graph))\n", + "\n", + "# Do inference\n", + "sess = onnxruntime.InferenceSession(onnx_model.SerializeToString(),\n", + " providers=[\"CPUExecutionProvider\"])\n", + "\n", + "# Initialize tensors\n", + "#x = numpy.ones((1, 1, 8, 8), dtype=numpy.float32)\n", + "\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "\n", + "y = sess.run(None, {'x': x})[0]\n", + "Indices = sess.run(None, {'x': x})[1]\n", + "\n", + "print(\"X shape:\", x.shape)\n", + "print(\"X:\", x)\n", + "print(\"Y shape:\", y.shape)\n", + "print(\"Y:\", y)\n", + "print(\"Indices shape:\", Indices.shape)\n", + "print(\"Indices:\", Indices)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyOKOYLaMLVHJNovPEUX24CO", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.8" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem.ipynb b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem.ipynb new file mode 100644 index 00000000..48589aca --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem.ipynb @@ -0,0 +1,1635 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "id": "02a475ca", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (6.33.0)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.9.23)\n", + "Requirement already satisfied: packaging in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "from onnx import TensorProto\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "maxpool_output_name1 = \"Y\"\n", + "maxpool_output_name2 = \"Indices\"\n", + "\n", + "\n", + "# Create the ONNX model with maxpool operator\n", + "def create_maxpool_model(input_shape, output_shape, dtype, paddings):\n", + "\n", + " #Create \"input-rank\" input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_shape)\n", + "\n", + " # Create output tensor (final result after maxpool operation)\n", + " maxpool_output1 = onnx.helper.make_tensor_value_info(maxpool_output_name1, dtype, output_shape)\n", + " maxpool_output2 = onnx.helper.make_tensor_value_info(maxpool_output_name2, TensorProto.INT64, output_shape)\n", + "\n", + " # Define clip node\n", + " maxpool_node = onnx.helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[input1_name],\n", + " outputs=[maxpool_output_name1, maxpool_output_name2],\n", + " ceil_mode = 0,\n", + " dilations=[1,1],\n", + " kernel_shape=[3,3],\n", + " pads=paddings,\n", + " strides=[1, 1],\n", + " auto_pad='NOTSET',\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [maxpool_node],\n", + " \"Test_MaxPool\",\n", + " [input1],\n", + " [maxpool_output1, maxpool_output2],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)]) # Explicitly set opset to 13\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"maxpool.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"maxpool.onnx\")\n", + " return session\n", + "\n", + "def do_maxpool(x, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " indices_f = (np.array2string(output[1], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + "\n", + " # Display results\n", + " print(f\"X = \\n{x_f}\")\n", + " print(f\"Y = \\n{y_f}\")\n", + " print(f\"Indices = \\n{indices_f}\")\n", + "\n", + "def compute_MaxPool_output_shape(input_shape, kernel_shape, pads, dilation, strides):\n", + "\n", + " dx0, dx1, dx2, dx3 = input_shape\n", + " dw0, dw1 = kernel_shape\n", + "\n", + "\n", + " myalpha = dx2 + pads[0] + pads[2]\n", + " mybeta = dx3 + pads[1] + pads[3]\n", + " mytheta = (dilation[0] * (dw0 - 1)) + 1\n", + " mygamma = (dilation[1] * (dw1 - 1)) + 1\n", + "# dY_2 = \\left\\lfloor{(dX_2 + pad\\_shape[0] - dilations[0] * (kernel\\_shape[0] - 1) - 1) / (strides[0] + 1)}\\right\\rfloor\n", + " mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1)\n", + "# dY_3 = \\left\\lfloor{(dX_3 + pad\\_shape[1] - dilations[1] * (kernel\\_shape[1] - 1) - 1) / (strides[1] + 1)} \\right\\rfloor\n", + " mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1)\n", + " \n", + " output_shape = [dx0, dx1, mydy2, mydy3]\n", + "\n", + " return output_shape\n", + "\n", + "\n", + "def pad_4d_spatial_js(tensor, paddings):\n", + " \"\"\"\n", + " Pads the last two dimensions (H, W) of a 4D tensor.\n", + " paddings = (top, left, bottom, right)\n", + " \"\"\"\n", + " N, C, H, W = tensor.shape\n", + " top, left, bottom, right = paddings\n", + " \n", + " # 1. Calculate the new spatial dimensions\n", + " new_H = H + top + bottom\n", + " new_W = W + left + right\n", + " \n", + " # 2. Create the new 4D \"canvas\" of zeros\n", + " # The Batch (N) and Channel (C) dimensions remain the same\n", + "# padded_tensor = np.zeros((N, C, new_H, new_W), dtype=tensor.dtype)\n", + " padded_tensor = np.full((N, C, new_H, new_W), -np.inf)\n", + " \n", + " # 3. Insert the original tensor into the padded one\n", + " # We use \":\" for N and C to select all batches and channels\n", + " # We use slicing for the H and W dimensions\n", + " padded_tensor[:, :, top : top + H, left : left + W] = tensor\n", + " \n", + " return padded_tensor\n", + "\n", + "def extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilations, dW):\n", + " \"\"\"\n", + " Extracts the maximum value from a dilated window.\n", + " \n", + " Args:\n", + " X_p: The 2D padded input array.\n", + " m, n: Integer constants representing the current output position.\n", + " strides: Tuple (s_h, s_w) for step size.\n", + " dilations: Tuple (d_h, d_w) for spacing between window elements.\n", + " dW: Tuple (dW0, dW1) for the window height and width.\n", + " \"\"\"\n", + " dW0, dW1 = dW\n", + " s_h, s_w = strides\n", + " d_h, d_w = dilations\n", + " \n", + " # Calculate the base (top-left) coordinates based on the stride\n", + " base_h = m * s_h\n", + " base_w = n * s_w\n", + " \n", + " # Initialize max_val with the first element of the window (h=0, w=0)\n", + " max_val = X_p[b, c, base_h, base_w]\n", + " \n", + " # Iterate through the window defined by dW0 and dW1\n", + " for h in range(dW0):\n", + " for w in range(dW1):\n", + " # Calculate the current dilated coordinates\n", + " curr_h = base_h + h * d_h\n", + " curr_w = base_w + w * d_w\n", + " \n", + " # Extract the value at these coordinates\n", + " val = X_p[b, c, curr_h, curr_w]\n", + " \n", + " # Update the maximum found so far\n", + " if val > max_val:\n", + " max_val = val\n", + " \n", + " return max_val\n", + "\n", + "def MaxPool(X, kernel_shape, strides, dilation, pads):\n", + "\n", + " Y_dims = compute_MaxPool_output_shape(X.shape, kernel_shape, pads, dilation, strides)\n", + "\n", + " Y = np.zeros(Y_dims)\n", + "\n", + " X_p = pad_4d_spatial_js(X, pads)\n", + " dX_p0, dX_p1, dX_p2, dX_p3 = X_p.shape\n", + " dY_p0, dY_p1, dY_p2, dY_p3 = Y.shape\n", + "\n", + " xp_f = (np.array2string(X_p, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " print(f\"MaxPool: X_p = \\n{xp_f}\")\n", + "\n", + " for b in range(dX_p0):\n", + " for c in range(dX_p1): \n", + " for m in range(dY_p2):\n", + " for n in range(dY_p3):\n", + " Y[b, c, m, n] = extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilation, kernel_shape)\n", + "\n", + " Y_f = (np.array2string(Y, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " print(f\"MaxPool: Y = \\n{Y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 1.79600868, 3.17047056, 3.24585250,-0.70210242, 0.21493457, 3.88964306, 2.83984821, 7.66431059],\n", + " [-0.64609348, 5.68134645, 0.25121777,-0.57023099, 0.19189985, 6.35651387, 2.40120956, 3.44485406],\n", + " [ 3.90387908, 2.42988301, 3.58786652, 2.39758521, 2.99846054,10.92699375, 4.50539251, 0.59171866],\n", + " [ 2.87879519, 3.01687926, 2.37521792, 3.98320962, 6.39730190, 4.47257121, 0.92853818, 2.01846954],\n", + " [ 4.59294020, 0.83082117, 0.12288230, 4.91708411, 4.55202322, 7.22642684, 1.96294360, 4.72855007],\n", + " [ 5.98531149,-0.03705777, 0.62739196, 3.16546247, 5.73444619, 3.03724337, 2.03938838, 2.04420144],\n", + " [-0.45958542, 5.71864670, 3.28681662, 0.87607173, 4.43693032, 1.38606641, 3.77389501, 7.60625193],\n", + " [ 1.52890288, 7.84946994, 6.03473822, 2.41944794, 0.72211592, 2.87885244, 1.42214040, 1.74029770]]]]\n", + "Y = \n", + "[[[[ 5.68134645, 5.68134645, 3.58786652,10.92699375,10.92699375,10.92699375],\n", + " [ 5.68134645, 5.68134645, 6.39730190,10.92699375,10.92699375,10.92699375],\n", + " [ 4.59294020, 4.91708411, 6.39730190,10.92699375,10.92699375,10.92699375],\n", + " [ 5.98531149, 4.91708411, 6.39730190, 7.22642684, 7.22642684, 7.22642684],\n", + " [ 5.98531149, 5.71864670, 5.73444619, 7.22642684, 7.22642684, 7.60625193],\n", + " [ 7.84946994, 7.84946994, 6.03473822, 5.73444619, 5.73444619, 7.60625193]]]]\n", + "Indices = \n", + "[[[[ 9, 9,18,21,21,21],\n", + " [ 9, 9,28,21,21,21],\n", + " [32,35,28,21,21,21],\n", + " [40,35,28,37,37,37],\n", + " [40,49,44,37,37,55],\n", + " [57,57,58,44,44,55]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 1.79600868, 3.17047056, 3.24585250,-0.70210242, 0.21493457, 3.88964306, 2.83984821, 7.66431059],\n", + " [-0.64609348, 5.68134645, 0.25121777,-0.57023099, 0.19189985, 6.35651387, 2.40120956, 3.44485406],\n", + " [ 3.90387908, 2.42988301, 3.58786652, 2.39758521, 2.99846054,10.92699375, 4.50539251, 0.59171866],\n", + " [ 2.87879519, 3.01687926, 2.37521792, 3.98320962, 6.39730190, 4.47257121, 0.92853818, 2.01846954],\n", + " [ 4.59294020, 0.83082117, 0.12288230, 4.91708411, 4.55202322, 7.22642684, 1.96294360, 4.72855007],\n", + " [ 5.98531149,-0.03705777, 0.62739196, 3.16546247, 5.73444619, 3.03724337, 2.03938838, 2.04420144],\n", + " [-0.45958542, 5.71864670, 3.28681662, 0.87607173, 4.43693032, 1.38606641, 3.77389501, 7.60625193],\n", + " [ 1.52890288, 7.84946994, 6.03473822, 2.41944794, 0.72211592, 2.87885244, 1.42214040, 1.74029770]]]]\n", + "MaxPool: Y = \n", + "[[[[ 5.68134645, 5.68134645, 3.58786652,10.92699375,10.92699375,10.92699375],\n", + " [ 5.68134645, 5.68134645, 6.39730190,10.92699375,10.92699375,10.92699375],\n", + " [ 4.59294020, 4.91708411, 6.39730190,10.92699375,10.92699375,10.92699375],\n", + " [ 5.98531149, 4.91708411, 6.39730190, 7.22642684, 7.22642684, 7.22642684],\n", + " [ 5.98531149, 5.71864670, 5.73444619, 7.22642684, 7.22642684, 7.60625193],\n", + " [ 7.84946994, 7.84946994, 6.03473822, 5.73444619, 5.73444619, 7.60625193]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [0,0,0,0]\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "135748cb-b557-4302-84f2-d94e5f9e9ca4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 5.13036980,-0.75398416, 4.56682130, 3.98975816, 2.34237621, 1.07973749, 4.45731956,-0.81557314],\n", + " [ 0.27041389, 0.86839577, 1.41787814, 2.40521624, 0.61740179, 1.13739145, 2.33768741, 6.12945841],\n", + " [ 2.24232462, 3.60132765, 5.11577101, 2.36663167,-0.78807371, 3.85064745, 0.33751639, 4.79362935],\n", + " [-2.93080046, 6.06238572, 2.95884444, 4.09228087, 2.97917384, 4.44342584, 2.97723800, 6.89381799],\n", + " [ 4.06489224, 3.71419092, 1.19594802, 2.25922909,-2.62004130,-0.62712872,-1.09160243, 5.54184707],\n", + " [ 2.38458137, 3.08885415, 4.13964478, 5.76753808, 3.86394633, 3.24441708, 1.53291187,-2.09198368],\n", + " [ 5.96249215, 4.45656932, 0.94222963, 4.99413990,-1.28668362, 2.56921295, 3.19327014, 5.54119022],\n", + " [ 2.64363945, 3.71308955, 4.37333373, 1.49939608, 3.42357271, 0.26289900, 3.94289309, 6.77464645]]]]\n", + "Y = \n", + "[[[[5.13036980,4.56682130,4.56682130,3.98975816,4.45731956,6.12945841],\n", + " [5.13036980,5.11577101,5.11577101,3.98975816,4.45731956,6.12945841],\n", + " [6.06238572,6.06238572,5.11577101,4.44342584,4.44342584,6.89381799],\n", + " [6.06238572,6.06238572,5.11577101,4.44342584,4.44342584,6.89381799],\n", + " [6.06238572,6.06238572,5.76753808,5.76753808,4.44342584,6.89381799],\n", + " [5.96249215,5.76753808,5.76753808,5.76753808,3.86394633,5.54184707],\n", + " [5.96249215,5.76753808,5.76753808,5.76753808,3.94289309,6.77464645],\n", + " [5.96249215,4.99413990,4.99413990,4.99413990,3.94289309,6.77464645]]]]\n", + "Indices = \n", + "[[[[ 0, 2, 2, 3, 6,15],\n", + " [ 0,18,18, 3, 6,15],\n", + " [25,25,18,29,29,31],\n", + " [25,25,18,29,29,31],\n", + " [25,25,43,43,29,31],\n", + " [48,43,43,43,44,39],\n", + " [48,43,43,43,62,63],\n", + " [48,51,51,51,62,63]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf],\n", + " [ 5.13036980,-0.75398416, 4.56682130, 3.98975816, 2.34237621, 1.07973749, 4.45731956,-0.81557314],\n", + " [ 0.27041389, 0.86839577, 1.41787814, 2.40521624, 0.61740179, 1.13739145, 2.33768741, 6.12945841],\n", + " [ 2.24232462, 3.60132765, 5.11577101, 2.36663167,-0.78807371, 3.85064745, 0.33751639, 4.79362935],\n", + " [-2.93080046, 6.06238572, 2.95884444, 4.09228087, 2.97917384, 4.44342584, 2.97723800, 6.89381799],\n", + " [ 4.06489224, 3.71419092, 1.19594802, 2.25922909,-2.62004130,-0.62712872,-1.09160243, 5.54184707],\n", + " [ 2.38458137, 3.08885415, 4.13964478, 5.76753808, 3.86394633, 3.24441708, 1.53291187,-2.09198368],\n", + " [ 5.96249215, 4.45656932, 0.94222963, 4.99413990,-1.28668362, 2.56921295, 3.19327014, 5.54119022],\n", + " [ 2.64363945, 3.71308955, 4.37333373, 1.49939608, 3.42357271, 0.26289900, 3.94289309, 6.77464645],\n", + " [ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[5.13036980,4.56682130,4.56682130,3.98975816,4.45731956,6.12945841],\n", + " [5.13036980,5.11577101,5.11577101,3.98975816,4.45731956,6.12945841],\n", + " [6.06238572,6.06238572,5.11577101,4.44342584,4.44342584,6.89381799],\n", + " [6.06238572,6.06238572,5.11577101,4.44342584,4.44342584,6.89381799],\n", + " [6.06238572,6.06238572,5.76753808,5.76753808,4.44342584,6.89381799],\n", + " [5.96249215,5.76753808,5.76753808,5.76753808,3.86394633,5.54184707],\n", + " [5.96249215,5.76753808,5.76753808,5.76753808,3.94289309,6.77464645],\n", + " [5.96249215,4.99413990,4.99413990,4.99413990,3.94289309,6.77464645]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [1,0,1,0]\n", + "session = create_maxpool_model(x.shape, [1, 1, 8, 6], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "334307f6-0318-4be2-8b13-ff044b314715", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-1.57535081, 0.19815172, 3.94942444, 4.05565170, 5.22354828, 3.75352392, 5.09895869, 1.82546961],\n", + " [ 0.96238148,-0.94115081, 4.12284764, 5.19764488,-0.22567474, 1.67775259, 2.25931363, 4.54326553],\n", + " [ 5.06451827,-1.35829665, 1.97454008, 2.93941081, 2.02587214, 4.88640497, 5.11783828, 1.71474916],\n", + " [ 2.67883287, 2.36978894, 1.82021970, 6.48741245, 3.19498230,-2.92284077, 7.60259205, 7.65933107],\n", + " [ 0.52292969, 5.86192032, 4.26640835, 5.78413283, 2.76844640, 1.21281288, 1.49251739,-0.04344814],\n", + " [ 5.43127052, 1.32332627, 5.89413405, 4.54178421,-2.02457545, 4.09267119,-1.90543088, 5.44447556],\n", + " [ 3.68443649, 3.41901403, 3.61283468, 3.76654174, 5.21036510, 1.48148403, 3.19905972, 3.34385306],\n", + " [ 1.29208975, 2.03278919,-0.42120315, 4.98724429, 1.76065030, 2.17176464, 3.04800854, 0.40389047]]]]\n", + "Y = \n", + "[[[[5.06451827,5.06451827,5.19764488,5.22354828,5.22354828,5.22354828,5.11783828,5.11783828],\n", + " [5.06451827,5.06451827,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.86192032,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.89413405,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.89413405,5.89413405,5.89413405,5.78413283,5.21036510,5.44447556,5.44447556],\n", + " [5.43127052,5.89413405,5.89413405,5.89413405,5.21036510,5.21036510,5.44447556,5.44447556]]]]\n", + "Indices = \n", + "[[[[16,16,11, 4, 4, 4,22,22],\n", + " [16,16,27,27,27,30,31,31],\n", + " [33,33,27,27,27,30,31,31],\n", + " [33,42,27,27,27,30,31,31],\n", + " [33,42,42,42,35,52,47,47],\n", + " [40,42,42,42,52,52,47,47]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf,-1.57535081, 0.19815172, 3.94942444, 4.05565170, 5.22354828, 3.75352392, 5.09895869, 1.82546961, -inf],\n", + " [ -inf, 0.96238148,-0.94115081, 4.12284764, 5.19764488,-0.22567474, 1.67775259, 2.25931363, 4.54326553, -inf],\n", + " [ -inf, 5.06451827,-1.35829665, 1.97454008, 2.93941081, 2.02587214, 4.88640497, 5.11783828, 1.71474916, -inf],\n", + " [ -inf, 2.67883287, 2.36978894, 1.82021970, 6.48741245, 3.19498230,-2.92284077, 7.60259205, 7.65933107, -inf],\n", + " [ -inf, 0.52292969, 5.86192032, 4.26640835, 5.78413283, 2.76844640, 1.21281288, 1.49251739,-0.04344814, -inf],\n", + " [ -inf, 5.43127052, 1.32332627, 5.89413405, 4.54178421,-2.02457545, 4.09267119,-1.90543088, 5.44447556, -inf],\n", + " [ -inf, 3.68443649, 3.41901403, 3.61283468, 3.76654174, 5.21036510, 1.48148403, 3.19905972, 3.34385306, -inf],\n", + " [ -inf, 1.29208975, 2.03278919,-0.42120315, 4.98724429, 1.76065030, 2.17176464, 3.04800854, 0.40389047, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[5.06451827,5.06451827,5.19764488,5.22354828,5.22354828,5.22354828,5.11783828,5.11783828],\n", + " [5.06451827,5.06451827,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.86192032,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.89413405,6.48741245,6.48741245,6.48741245,7.60259205,7.65933107,7.65933107],\n", + " [5.86192032,5.89413405,5.89413405,5.89413405,5.78413283,5.21036510,5.44447556,5.44447556],\n", + " [5.43127052,5.89413405,5.89413405,5.89413405,5.21036510,5.21036510,5.44447556,5.44447556]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [0,1,0,1]\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 8], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "9d381618-d21b-4c17-827e-bf4c87f96417", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 4.98607334, 4.43451312, 5.46418744, 7.96062541, 7.32356011, 0.83885246, 2.84992185, 3.18479986],\n", + " [ 1.60812762, 1.65416875,-1.66700338, 2.83156972, 3.78214066, 2.51950782, 3.32110566, 1.74367219],\n", + " [ 3.60167983, 1.44913491, 1.55946730,-0.09510786, 1.06540049, 4.26294296,-3.73937871,-0.14324033],\n", + " [ 4.57257058, 2.88705675, 7.42822107, 2.87263169, 1.44952849,-0.21843020, 3.39916466,-0.96708609],\n", + " [ 2.73243618, 2.42057721, 1.09960891, 2.18663321, 2.80399063, 2.29423523, 1.84706645, 3.45780033],\n", + " [ 2.26660083, 4.35503051, 2.95268915, 4.23509706, 0.29842976, 5.18785400, 3.64514241, 3.84870089],\n", + " [ 9.92767698, 2.58206290, 4.41543398, 3.32662113, 6.45969103,-0.02747886, 3.39070703, 2.31965640],\n", + " [ 4.71011400, 5.05292920,-0.72234879, 0.30617864, 6.38084397, 2.64023278, 2.03306703, 3.86347293]]]]\n", + "Y = \n", + "[[[[4.98607334,5.46418744,7.96062541,7.96062541,7.96062541,7.32356011,3.32110566,3.32110566],\n", + " [4.98607334,5.46418744,7.96062541,7.96062541,7.96062541,7.32356011,4.26294296,3.32110566],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,4.26294296,4.26294296,4.26294296,3.39916466],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,4.26294296,4.26294296,4.26294296,3.45780033],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,5.18785400,5.18785400,5.18785400,3.84870089],\n", + " [9.92767698,9.92767698,4.41543398,6.45969103,6.45969103,6.45969103,5.18785400,3.84870089],\n", + " [9.92767698,9.92767698,5.05292920,6.45969103,6.45969103,6.45969103,5.18785400,3.86347293],\n", + " [9.92767698,9.92767698,5.05292920,6.45969103,6.45969103,6.45969103,3.86347293,3.86347293]]]]\n", + "Indices = \n", + "[[[[ 0, 2, 3, 3, 3, 4,14,14],\n", + " [ 0, 2, 3, 3, 3, 4,21,14],\n", + " [24,26,26,26,21,21,21,30],\n", + " [24,26,26,26,21,21,21,39],\n", + " [24,26,26,26,45,45,45,47],\n", + " [48,48,50,52,52,52,45,47],\n", + " [48,48,57,52,52,52,45,63],\n", + " [48,48,57,52,52,52,63,63]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf],\n", + " [ -inf, 4.98607334, 4.43451312, 5.46418744, 7.96062541, 7.32356011, 0.83885246, 2.84992185, 3.18479986, -inf],\n", + " [ -inf, 1.60812762, 1.65416875,-1.66700338, 2.83156972, 3.78214066, 2.51950782, 3.32110566, 1.74367219, -inf],\n", + " [ -inf, 3.60167983, 1.44913491, 1.55946730,-0.09510786, 1.06540049, 4.26294296,-3.73937871,-0.14324033, -inf],\n", + " [ -inf, 4.57257058, 2.88705675, 7.42822107, 2.87263169, 1.44952849,-0.21843020, 3.39916466,-0.96708609, -inf],\n", + " [ -inf, 2.73243618, 2.42057721, 1.09960891, 2.18663321, 2.80399063, 2.29423523, 1.84706645, 3.45780033, -inf],\n", + " [ -inf, 2.26660083, 4.35503051, 2.95268915, 4.23509706, 0.29842976, 5.18785400, 3.64514241, 3.84870089, -inf],\n", + " [ -inf, 9.92767698, 2.58206290, 4.41543398, 3.32662113, 6.45969103,-0.02747886, 3.39070703, 2.31965640, -inf],\n", + " [ -inf, 4.71011400, 5.05292920,-0.72234879, 0.30617864, 6.38084397, 2.64023278, 2.03306703, 3.86347293, -inf],\n", + " [ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[4.98607334,5.46418744,7.96062541,7.96062541,7.96062541,7.32356011,3.32110566,3.32110566],\n", + " [4.98607334,5.46418744,7.96062541,7.96062541,7.96062541,7.32356011,4.26294296,3.32110566],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,4.26294296,4.26294296,4.26294296,3.39916466],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,4.26294296,4.26294296,4.26294296,3.45780033],\n", + " [4.57257058,7.42822107,7.42822107,7.42822107,5.18785400,5.18785400,5.18785400,3.84870089],\n", + " [9.92767698,9.92767698,4.41543398,6.45969103,6.45969103,6.45969103,5.18785400,3.84870089],\n", + " [9.92767698,9.92767698,5.05292920,6.45969103,6.45969103,6.45969103,5.18785400,3.86347293],\n", + " [9.92767698,9.92767698,5.05292920,6.45969103,6.45969103,6.45969103,3.86347293,3.86347293]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [1,1,1,1]\n", + "session = create_maxpool_model(x.shape, [1, 1, 8, 8], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "c5b58583-e9cc-49c8-9dfa-cc3e16781ba3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.75975839, 0.63218521, 8.35759425, 4.61511272, 8.51478077, 6.81711875, 4.63548504, 3.52561379],\n", + " [-1.32765504,-0.15931693, 2.00709488, 1.34298770, 1.46112165, 1.55408602, 4.90735235, 5.58452782],\n", + " [ 4.93443325, 2.83761657, 5.75154303, 3.34446519, 5.02084812, 4.84546163, 1.28441732, 3.22493839],\n", + " [ 0.77167803,-0.27540048, 1.37917745, 1.38754982, 1.19622917, 5.35550624, 6.66358844, 4.83410033],\n", + " [ 0.99303323,-0.29535120, 1.95815551, 4.43278847, 0.19959602, 0.57786140, 2.15687216, 6.19514133],\n", + " [-0.29973321, 1.87410672, 3.68007141, 4.52717912, 1.84257320, 2.83896877, 3.30248328, 3.85395828],\n", + " [ 3.74535247, 5.72413985, 7.60905102, 0.26781042, 0.85889725, 2.21418779, 4.92636673,-0.56387039],\n", + " [-0.03253864, 4.93199984, 4.68944788, 4.35129000, 4.00183387, 5.58044461, 2.40325486, 2.06543036]]]]\n", + "Y = \n", + "[[[[8.35759425,8.35759425,8.51478077,8.51478077,8.51478077,6.81711875],\n", + " [8.35759425,8.35759425,8.51478077,8.51478077,8.51478077,6.81711875],\n", + " [5.75154303,5.75154303,5.75154303,5.35550624,6.66358844,6.66358844],\n", + " [5.75154303,5.75154303,5.75154303,5.35550624,6.66358844,6.66358844],\n", + " [3.68007141,4.52717912,4.52717912,5.35550624,6.66358844,6.66358844],\n", + " [7.60905102,7.60905102,7.60905102,4.52717912,4.92636673,6.19514133],\n", + " [7.60905102,7.60905102,7.60905102,5.58044461,5.58044461,5.58044461]]]]\n", + "Indices = \n", + "[[[[ 2, 2, 4, 4, 4, 5],\n", + " [ 2, 2, 4, 4, 4, 5],\n", + " [18,18,18,29,30,30],\n", + " [18,18,18,29,30,30],\n", + " [42,43,43,29,30,30],\n", + " [50,50,50,43,54,39],\n", + " [50,50,50,61,61,61]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf],\n", + " [ 3.75975839, 0.63218521, 8.35759425, 4.61511272, 8.51478077, 6.81711875, 4.63548504, 3.52561379],\n", + " [-1.32765504,-0.15931693, 2.00709488, 1.34298770, 1.46112165, 1.55408602, 4.90735235, 5.58452782],\n", + " [ 4.93443325, 2.83761657, 5.75154303, 3.34446519, 5.02084812, 4.84546163, 1.28441732, 3.22493839],\n", + " [ 0.77167803,-0.27540048, 1.37917745, 1.38754982, 1.19622917, 5.35550624, 6.66358844, 4.83410033],\n", + " [ 0.99303323,-0.29535120, 1.95815551, 4.43278847, 0.19959602, 0.57786140, 2.15687216, 6.19514133],\n", + " [-0.29973321, 1.87410672, 3.68007141, 4.52717912, 1.84257320, 2.83896877, 3.30248328, 3.85395828],\n", + " [ 3.74535247, 5.72413985, 7.60905102, 0.26781042, 0.85889725, 2.21418779, 4.92636673,-0.56387039],\n", + " [-0.03253864, 4.93199984, 4.68944788, 4.35129000, 4.00183387, 5.58044461, 2.40325486, 2.06543036]]]]\n", + "MaxPool: Y = \n", + "[[[[8.35759425,8.35759425,8.51478077,8.51478077,8.51478077,6.81711875],\n", + " [8.35759425,8.35759425,8.51478077,8.51478077,8.51478077,6.81711875],\n", + " [5.75154303,5.75154303,5.75154303,5.35550624,6.66358844,6.66358844],\n", + " [5.75154303,5.75154303,5.75154303,5.35550624,6.66358844,6.66358844],\n", + " [3.68007141,4.52717912,4.52717912,5.35550624,6.66358844,6.66358844],\n", + " [7.60905102,7.60905102,7.60905102,4.52717912,4.92636673,6.19514133],\n", + " [7.60905102,7.60905102,7.60905102,5.58044461,5.58044461,5.58044461]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [1,0,0,0]\n", + "session = create_maxpool_model(x.shape, [1, 1, 7, 6], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "5cb142b2-9fc4-40d2-a251-9f933a67a985", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 6.68758997, 3.44034635, 3.06023204,-2.17719825, 1.32991274, 6.48189503, 0.62419768, 2.76308118],\n", + " [-2.20121786, 4.86027483, 0.58905797, 6.38904686, 2.98429462, 0.77338476,-1.60887085, 1.63812894],\n", + " [ 6.81845954, 6.22773524, 0.58155682, 5.36573872,-1.18755163, 6.70753747,-0.50645250, 1.95861228],\n", + " [ 4.97209458, 1.46070623, 3.81461452, 0.96006355, 0.89146504, 3.29981580, 0.51805791, 5.47982492],\n", + " [10.28648831, 3.97566445, 2.29908000, 1.19967425, 5.38379523, 2.84386909, 4.06605147, 1.29910292],\n", + " [ 5.14187115, 1.58952241, 5.01212996, 6.00465687, 7.99565434,-2.08399700, 5.81166604, 0.76170753],\n", + " [ 3.07093120, 6.47080374,-0.52684661, 4.40289975,-4.30620774, 3.63044199, 2.59682099, 2.11699694],\n", + " [ 2.81897369, 2.81738171, 4.60509516, 4.90102861, 4.98890190, 3.87693076, 3.37442063, 2.14076499]]]]\n", + "Y = \n", + "[[[[ 6.81845954, 6.38904686, 6.38904686, 6.70753747, 6.70753747, 6.70753747],\n", + " [ 6.81845954, 6.38904686, 6.38904686, 6.70753747, 6.70753747, 6.70753747],\n", + " [10.28648831, 6.22773524, 5.38379523, 6.70753747, 6.70753747, 6.70753747],\n", + " [10.28648831, 6.00465687, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [10.28648831, 6.47080374, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [ 6.47080374, 6.47080374, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [ 6.47080374, 6.47080374, 4.98890190, 4.98890190, 4.98890190, 3.87693076]]]]\n", + "Indices = \n", + "[[[[16,11,11,21,21,21],\n", + " [16,11,11,21,21,21],\n", + " [32,17,36,21,21,21],\n", + " [32,43,44,44,44,46],\n", + " [32,49,44,44,44,46],\n", + " [49,49,44,44,44,46],\n", + " [49,49,60,60,60,61]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 6.68758997, 3.44034635, 3.06023204,-2.17719825, 1.32991274, 6.48189503, 0.62419768, 2.76308118],\n", + " [-2.20121786, 4.86027483, 0.58905797, 6.38904686, 2.98429462, 0.77338476,-1.60887085, 1.63812894],\n", + " [ 6.81845954, 6.22773524, 0.58155682, 5.36573872,-1.18755163, 6.70753747,-0.50645250, 1.95861228],\n", + " [ 4.97209458, 1.46070623, 3.81461452, 0.96006355, 0.89146504, 3.29981580, 0.51805791, 5.47982492],\n", + " [10.28648831, 3.97566445, 2.29908000, 1.19967425, 5.38379523, 2.84386909, 4.06605147, 1.29910292],\n", + " [ 5.14187115, 1.58952241, 5.01212996, 6.00465687, 7.99565434,-2.08399700, 5.81166604, 0.76170753],\n", + " [ 3.07093120, 6.47080374,-0.52684661, 4.40289975,-4.30620774, 3.63044199, 2.59682099, 2.11699694],\n", + " [ 2.81897369, 2.81738171, 4.60509516, 4.90102861, 4.98890190, 3.87693076, 3.37442063, 2.14076499],\n", + " [ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[ 6.81845954, 6.38904686, 6.38904686, 6.70753747, 6.70753747, 6.70753747],\n", + " [ 6.81845954, 6.38904686, 6.38904686, 6.70753747, 6.70753747, 6.70753747],\n", + " [10.28648831, 6.22773524, 5.38379523, 6.70753747, 6.70753747, 6.70753747],\n", + " [10.28648831, 6.00465687, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [10.28648831, 6.47080374, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [ 6.47080374, 6.47080374, 7.99565434, 7.99565434, 7.99565434, 5.81166604],\n", + " [ 6.47080374, 6.47080374, 4.98890190, 4.98890190, 4.98890190, 3.87693076]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [0,0,1,0]\n", + "session = create_maxpool_model(x.shape, [1, 1, 7, 6], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "14072deb-c1ea-4cab-92b1-9b4f364f95cc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.81298232, 0.34283475, 1.14314620, 2.97358787, 5.66563291, 4.86075205, 5.58519916, 3.69097858],\n", + " [ 1.32598305, 6.83778017,11.80664148, 7.32146590, 2.68302092, 3.82571716, 6.76203895, 5.49982725],\n", + " [ 6.01340078,-0.15222185, 7.03114049, 1.63563753, 1.34102856, 1.92684096, 3.91260788, 4.19297329],\n", + " [ 0.18409862, 6.85141257, 0.31444599, 2.90975693, 3.34028908, 2.56295336, 6.16126852, 3.42714257],\n", + " [ 4.54961394, 5.90543393, 0.86876104, 2.08616746, 6.39554545, 3.66527199, 2.51528368, 3.79772345],\n", + " [-0.55021176, 2.32133683, 0.74765162, 3.38853220, 6.89484055, 3.78284987, 2.88039705, 5.93622064],\n", + " [ 6.88611291, 0.73910469, 0.67937796,-0.38731361, 3.33262262, 2.54048657, 5.80065684,-1.95613297],\n", + " [ 6.41852181, 3.43122619, 5.76366891, 0.77693951, 4.19584751, 4.94815978, 3.27727868, 1.92328037]]]]\n", + "Y = \n", + "[[[[ 6.83778017,11.80664148,11.80664148,11.80664148, 7.32146590, 6.76203895, 6.76203895],\n", + " [ 6.85141257,11.80664148,11.80664148,11.80664148, 7.32146590, 6.76203895, 6.76203895],\n", + " [ 6.85141257, 7.03114049, 7.03114049, 7.03114049, 6.39554545, 6.39554545, 6.16126852],\n", + " [ 6.85141257, 6.85141257, 6.85141257, 6.89484055, 6.89484055, 6.89484055, 6.16126852],\n", + " [ 6.88611291, 6.88611291, 5.90543393, 6.89484055, 6.89484055, 6.89484055, 5.93622064],\n", + " [ 6.88611291, 6.88611291, 5.76366891, 6.89484055, 6.89484055, 6.89484055, 5.93622064]]]]\n", + "Indices = \n", + "[[[[ 9,10,10,10,11,14,14],\n", + " [25,10,10,10,11,14,14],\n", + " [25,18,18,18,36,36,30],\n", + " [25,25,25,44,44,44,30],\n", + " [48,48,33,44,44,44,47],\n", + " [48,48,58,44,44,44,47]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, 3.81298232, 0.34283475, 1.14314620, 2.97358787, 5.66563291, 4.86075205, 5.58519916, 3.69097858],\n", + " [ -inf, 1.32598305, 6.83778017,11.80664148, 7.32146590, 2.68302092, 3.82571716, 6.76203895, 5.49982725],\n", + " [ -inf, 6.01340078,-0.15222185, 7.03114049, 1.63563753, 1.34102856, 1.92684096, 3.91260788, 4.19297329],\n", + " [ -inf, 0.18409862, 6.85141257, 0.31444599, 2.90975693, 3.34028908, 2.56295336, 6.16126852, 3.42714257],\n", + " [ -inf, 4.54961394, 5.90543393, 0.86876104, 2.08616746, 6.39554545, 3.66527199, 2.51528368, 3.79772345],\n", + " [ -inf,-0.55021176, 2.32133683, 0.74765162, 3.38853220, 6.89484055, 3.78284987, 2.88039705, 5.93622064],\n", + " [ -inf, 6.88611291, 0.73910469, 0.67937796,-0.38731361, 3.33262262, 2.54048657, 5.80065684,-1.95613297],\n", + " [ -inf, 6.41852181, 3.43122619, 5.76366891, 0.77693951, 4.19584751, 4.94815978, 3.27727868, 1.92328037]]]]\n", + "MaxPool: Y = \n", + "[[[[ 6.83778017,11.80664148,11.80664148,11.80664148, 7.32146590, 6.76203895, 6.76203895],\n", + " [ 6.85141257,11.80664148,11.80664148,11.80664148, 7.32146590, 6.76203895, 6.76203895],\n", + " [ 6.85141257, 7.03114049, 7.03114049, 7.03114049, 6.39554545, 6.39554545, 6.16126852],\n", + " [ 6.85141257, 6.85141257, 6.85141257, 6.89484055, 6.89484055, 6.89484055, 6.16126852],\n", + " [ 6.88611291, 6.88611291, 5.90543393, 6.89484055, 6.89484055, 6.89484055, 5.93622064],\n", + " [ 6.88611291, 6.88611291, 5.76366891, 6.89484055, 6.89484055, 6.89484055, 5.93622064]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [0,1,0,0]\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 7], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "3283a6e3-b1ae-49c4-afa7-32d737d6bd16", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.77215090, 4.75723838, 8.55781965, 0.78615983, 0.12722420,-1.06758180, 1.72740480, 2.67392968],\n", + " [ 5.82006760, 7.82941061, 7.07331734, 3.18499969, 2.08565643, 1.86802229, 3.76434712, 5.02928733],\n", + " [ 5.10946681, 4.26304666, 7.60508046,-0.52712479, 0.94497943,-2.72429789, 4.82980308, 1.43855883],\n", + " [ 0.30282386, 6.97465706, 2.66909992,-1.19734410, 1.78544601, 4.08093680, 0.90952255, 5.03971128],\n", + " [ 1.87309120, 2.21011854, 0.53187335, 1.92701634, 5.09813924, 3.41752264, 1.56399777,-0.78419870],\n", + " [ 3.27418685, 0.39654103,-0.41107836, 1.76179774, 8.16411925, 2.75642098,-1.24161395, 3.86984014],\n", + " [-1.84656724, 1.99188346, 3.25518986,-2.91215305, 5.54321520, 4.55729149, 0.06518238, 8.22609852],\n", + " [-0.71691664,-0.59373537, 3.78832115, 3.45530158, 4.62909760, 6.32596129, 5.05374653, 3.49156719]]]]\n", + "Y = \n", + "[[[[8.55781965,8.55781965,8.55781965,3.18499969,4.82980308,5.02928733,5.02928733],\n", + " [7.82941061,7.82941061,7.60508046,4.08093680,4.82980308,5.03971128,5.03971128],\n", + " [7.60508046,7.60508046,7.60508046,5.09813924,5.09813924,5.03971128,5.03971128],\n", + " [6.97465706,6.97465706,8.16411925,8.16411925,8.16411925,5.03971128,5.03971128],\n", + " [3.27418685,3.25518986,8.16411925,8.16411925,8.16411925,8.22609852,8.22609852],\n", + " [3.78832115,3.78832115,8.16411925,8.16411925,8.16411925,8.22609852,8.22609852]]]]\n", + "Indices = \n", + "[[[[ 2, 2, 2,11,22,15,15],\n", + " [ 9, 9,18,29,22,31,31],\n", + " [18,18,18,36,36,31,31],\n", + " [25,25,44,44,44,31,31],\n", + " [40,50,44,44,44,55,55],\n", + " [58,58,44,44,44,55,55]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.77215090, 4.75723838, 8.55781965, 0.78615983, 0.12722420,-1.06758180, 1.72740480, 2.67392968, -inf],\n", + " [ 5.82006760, 7.82941061, 7.07331734, 3.18499969, 2.08565643, 1.86802229, 3.76434712, 5.02928733, -inf],\n", + " [ 5.10946681, 4.26304666, 7.60508046,-0.52712479, 0.94497943,-2.72429789, 4.82980308, 1.43855883, -inf],\n", + " [ 0.30282386, 6.97465706, 2.66909992,-1.19734410, 1.78544601, 4.08093680, 0.90952255, 5.03971128, -inf],\n", + " [ 1.87309120, 2.21011854, 0.53187335, 1.92701634, 5.09813924, 3.41752264, 1.56399777,-0.78419870, -inf],\n", + " [ 3.27418685, 0.39654103,-0.41107836, 1.76179774, 8.16411925, 2.75642098,-1.24161395, 3.86984014, -inf],\n", + " [-1.84656724, 1.99188346, 3.25518986,-2.91215305, 5.54321520, 4.55729149, 0.06518238, 8.22609852, -inf],\n", + " [-0.71691664,-0.59373537, 3.78832115, 3.45530158, 4.62909760, 6.32596129, 5.05374653, 3.49156719, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[8.55781965,8.55781965,8.55781965,3.18499969,4.82980308,5.02928733,5.02928733],\n", + " [7.82941061,7.82941061,7.60508046,4.08093680,4.82980308,5.03971128,5.03971128],\n", + " [7.60508046,7.60508046,7.60508046,5.09813924,5.09813924,5.03971128,5.03971128],\n", + " [6.97465706,6.97465706,8.16411925,8.16411925,8.16411925,5.03971128,5.03971128],\n", + " [3.27418685,3.25518986,8.16411925,8.16411925,8.16411925,8.22609852,8.22609852],\n", + " [3.78832115,3.78832115,8.16411925,8.16411925,8.16411925,8.22609852,8.22609852]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "pads = [0,0,0,1]\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 7], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "aa272a71-032d-4467-95e5-8587a195576f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 2, 3,-4, 7,-1, 4, 4, 5],\n", + " [ 3, 6, 0, 2, 2, 2, 8, 3],\n", + " [ 1, 7, 1, 6,-1, 1, 6, 2],\n", + " [ 2, 0, 0, 4, 3, 1, 3, 5],\n", + " [ 5, 6, 2, 5,-1, 3, 1, 3],\n", + " [ 0, 6, 2,-4, 0, 1, 5, 4],\n", + " [ 1, 0, 0, 3, 8, 4, 4, 3],\n", + " [ 2, 9, 6,-1, 4, 1, 6, 3]]]]\n", + "Y = \n", + "[[[[7,7,7,7,8,8],\n", + " [7,7,6,6,8,8],\n", + " [7,7,6,6,6,6],\n", + " [6,6,5,5,5,5],\n", + " [6,6,8,8,8,5],\n", + " [9,9,8,8,8,6]]]]\n", + "Indices = \n", + "[[[[17, 3, 3, 3,14,14],\n", + " [17,17,19,19,14,14],\n", + " [17,17,19,19,22,22],\n", + " [33,33,35,35,46,31],\n", + " [33,33,52,52,52,46],\n", + " [57,57,52,52,52,62]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 2.00000000, 3.00000000,-4.00000000, 7.00000000,-1.00000000, 4.00000000, 4.00000000, 5.00000000],\n", + " [ 3.00000000, 6.00000000, 0.00000000, 2.00000000, 2.00000000, 2.00000000, 8.00000000, 3.00000000],\n", + " [ 1.00000000, 7.00000000, 1.00000000, 6.00000000,-1.00000000, 1.00000000, 6.00000000, 2.00000000],\n", + " [ 2.00000000, 0.00000000, 0.00000000, 4.00000000, 3.00000000, 1.00000000, 3.00000000, 5.00000000],\n", + " [ 5.00000000, 6.00000000, 2.00000000, 5.00000000,-1.00000000, 3.00000000, 1.00000000, 3.00000000],\n", + " [ 0.00000000, 6.00000000, 2.00000000,-4.00000000, 0.00000000, 1.00000000, 5.00000000, 4.00000000],\n", + " [ 1.00000000, 0.00000000, 0.00000000, 3.00000000, 8.00000000, 4.00000000, 4.00000000, 3.00000000],\n", + " [ 2.00000000, 9.00000000, 6.00000000,-1.00000000, 4.00000000, 1.00000000, 6.00000000, 3.00000000]]]]\n", + "MaxPool: Y = \n", + "[[[[7.00000000,7.00000000,7.00000000,7.00000000,8.00000000,8.00000000],\n", + " [7.00000000,7.00000000,6.00000000,6.00000000,8.00000000,8.00000000],\n", + " [7.00000000,7.00000000,6.00000000,6.00000000,6.00000000,6.00000000],\n", + " [6.00000000,6.00000000,5.00000000,5.00000000,5.00000000,5.00000000],\n", + " [6.00000000,6.00000000,8.00000000,8.00000000,8.00000000,5.00000000],\n", + " [9.00000000,9.00000000,8.00000000,8.00000000,8.00000000,6.00000000]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.INT8\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 8, 8))\n", + "x_int8 = np.clip(np.round(x), -128, 127).astype(np.int8)\n", + "pads = [0,0,0,0]\n", + "session = create_maxpool_model(x_int8.shape, [1, 1, 6, 6], onnx_type, pads)\n", + "do_maxpool(x_int8, session)\n", + "MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "1faf9757-cfcd-47e5-bf2d-e180f4f6b2ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[12,14,14,-1,15, 5,12,20],\n", + " [ 7, 3,10,10,13,-3, 9, 8],\n", + " [ 4,11,10, 3, 9,16,15,17],\n", + " [ 4,13,18, 9,15,13, 9,13],\n", + " [16,15, 9, 5, 7, 4,18,11],\n", + " [ 5, 4, 6,11,13,16, 6,10],\n", + " [17,12,18,11, 3,12,10,11],\n", + " [ 9, 4,17, 5,10,12,13, 6]]]]\n", + "Y = \n", + "[[[[14,14,14,15,15,15,20,20],\n", + " [14,14,14,15,16,16,20,20],\n", + " [13,18,18,18,16,16,17,17],\n", + " [16,18,18,18,16,18,18,18],\n", + " [16,18,18,18,16,18,18,18],\n", + " [17,18,18,18,16,18,18,18],\n", + " [17,18,18,18,16,16,16,13],\n", + " [17,18,18,18,12,13,13,13]]]]\n", + "Indices = \n", + "[[[[ 1, 1, 1, 4, 4, 4, 7, 7],\n", + " [ 1, 1, 1, 4,21,21, 7, 7],\n", + " [25,26,26,26,21,21,23,23],\n", + " [32,26,26,26,21,38,38,38],\n", + " [32,26,26,26,45,38,38,38],\n", + " [48,50,50,50,45,38,38,38],\n", + " [48,50,50,50,45,45,45,62],\n", + " [48,50,50,50,53,62,62,62]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.INT8\n", + "x = np.random.normal(10, 4, size=(1, 1, 8, 8))\n", + "x_int8 = np.clip(np.round(x), -128, 127).astype(np.int8)\n", + "pads = [1,1,1,1]\n", + "session = create_maxpool_model(x_int8.shape, [1, 1, 8, 8], onnx_type, pads)\n", + "do_maxpool(x_int8, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "ca13df47-d80a-473e-bb32-80f3ffa98e30", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[1,2,5,0,0,0,0,0],\n", + " [3,4,6,0,0,0,0,0],\n", + " [7,8,9,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0]]]]\n", + "Y = \n", + "[[[[4,6,6,6,0,0,0,0],\n", + " [8,9,9,9,0,0,0,0],\n", + " [8,9,9,9,0,0,0,0],\n", + " [8,9,9,9,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0],\n", + " [0,0,0,0,0,0,0,0]]]]\n", + "Indices = \n", + "[[[[ 9,10,10,10, 3, 4, 5, 6],\n", + " [17,18,18,18, 3, 4, 5, 6],\n", + " [17,18,18,18,11,12,13,14],\n", + " [17,18,18,18,19,20,21,22],\n", + " [24,24,25,26,27,28,29,30],\n", + " [32,32,33,34,35,36,37,38],\n", + " [40,40,41,42,43,44,45,46],\n", + " [48,48,49,50,51,52,53,54]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ 1, 2, 5, 0, 0, 0, 0, 0],\n", + " [ 3, 4, 6, 0, 0, 0, 0, 0],\n", + " [ 7, 8, 9, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "pads = [1,1,1,1]\n", + "session = create_maxpool_model(x_int8.shape, [1, 1, 8, 8], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "22792a33-f647-40c4-92e8-f06011ba76fa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-1,-2, 5, 0, 0, 0, 0, 0],\n", + " [-3,-4, 6, 0, 0, 0, 0, 0],\n", + " [ 7, 8, 9, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "Y = \n", + "[[[[-1, 6, 6, 6, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "Indices = \n", + "[[[[ 0,10,10,10, 3, 4, 5, 6],\n", + " [17,18,18,18, 3, 4, 5, 6],\n", + " [17,18,18,18,11,12,13,14],\n", + " [17,18,18,18,19,20,21,22],\n", + " [24,24,25,26,27,28,29,30],\n", + " [32,32,33,34,35,36,37,38],\n", + " [40,40,41,42,43,44,45,46],\n", + " [48,48,49,50,51,52,53,54]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ -1, -2, 5, 0, 0, 0, 0, 0],\n", + " [ -3, -4, 6, 0, 0, 0, 0, 0],\n", + " [ 7, 8, 9, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "pads = [1,1,1,1]\n", + "session = create_maxpool_model(x_int8.shape, [1, 1, 8, 8], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "6a6c45a0-6fa6-41dd-acdb-c86a1507eebd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-128,-128, 5, 0, 0, 0, 0, 0],\n", + " [-128,-128, 6, 0, 0, 0, 0, 0],\n", + " [ 7, 8, 9, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "Y = \n", + "[[[[-128, 6, 6, 6, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 8, 9, 9, 9, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "Indices = \n", + "[[[[-9,10,10,10, 3, 4, 5, 6],\n", + " [17,18,18,18, 3, 4, 5, 6],\n", + " [17,18,18,18,11,12,13,14],\n", + " [17,18,18,18,19,20,21,22],\n", + " [24,24,25,26,27,28,29,30],\n", + " [32,32,33,34,35,36,37,38],\n", + " [40,40,41,42,43,44,45,46],\n", + " [48,48,49,50,51,52,53,54]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ -128, -128, 5, 0, 0, 0, 0, 0],\n", + " [ -128, -128, 6, 0, 0, 0, 0, 0],\n", + " [ 7, 8, 9, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0],\n", + " [ 0, 0, 0, 0, 0, 0, 0, 0]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "pads = [1,1,1,1]\n", + "session = create_maxpool_model(x_int8.shape, [1, 1, 8, 8], onnx_type, pads)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "markdown", + "id": "1c8a25b9", + "metadata": {}, + "source": [ + "## No nominal cases (nan and inf values)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "518b148e-ec54-41c2-b427-6da82dabd28b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ nan, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, 1.67840886]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436,5.70581436],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,54,54],\n", + " [40,40,51,51,51,54,54,54],\n", + " [48,48,51,51,51,54,54,54]]]]\n", + "MaxPool: X_p = \n", + "[[[[ nan, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, 1.67840886]]]]\n", + "MaxPool: Y = \n", + "[[[[ nan,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.763249336 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.763272334 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN1:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ np.nan, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, 5.70581444, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, 1.67840883]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8a0e757d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, nan]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436,5.70581436],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,54,54],\n", + " [40,40,51,51,51,54,54,54],\n", + " [48,48,51,51,51,54,54,54]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, nan]]]]\n", + "MaxPool: Y = \n", + "[[[[6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.778044123 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.778061393 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN1:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ 3.15168382, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, 5.70581444, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, np.nan]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b3fe6691-2397-48ae-8a69-2c7e3f92a48e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ nan, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, nan,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, nan]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436,5.70581436],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,54,54],\n", + " [40,40,51,51,51,54,54,54],\n", + " [48,48,51,51,51,54,54,54]]]]\n", + "MaxPool: X_p = \n", + "[[[[ nan, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, nan,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, nan]]]]\n", + "MaxPool: Y = \n", + "[[[[ nan,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110, nan,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.793867967 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.793883488 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN1:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ np.nan, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, np.nan,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, 5.70581444, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, np.nan]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "e663f77e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, inf]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436,5.70581436],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,5.70581436, inf, inf],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,5.70581436, inf, inf]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,54,54],\n", + " [40,40,51,51,51,54,63,63],\n", + " [48,48,51,51,51,54,63,63]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, inf]]]]\n", + "MaxPool: Y = \n", + "[[[[6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,5.70581436, inf]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.809743684 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.809760852 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN2:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ 3.15168382, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, 5.70581444, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, np.inf]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1ca723dd", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, -inf]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436,5.70581436],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436,5.70581436]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,54,54],\n", + " [40,40,51,51,51,54,54,54],\n", + " [48,48,51,51,51,54,54,54]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, 5.70581436, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.70581436],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,5.70581436,5.70581436]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.824560261 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.824577736 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN3:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ 3.15168382, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, 5.70581444, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, -np.inf]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "efcf7015-5476-4ae2-bc7a-a8f26b798737", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, nan, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, -inf]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.06112671],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,3.19434452,3.19434452,2.55583477],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,2.43473148,1.84941924,1.84941924]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,38],\n", + " [40,40,51,51,51,45,45,46],\n", + " [48,48,51,51,51,52,62,62]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, nan, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,3.19434452,3.19434452]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.841950445 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.841967019 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN4:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ 3.15168382, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, np.nan, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, -np.inf]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "18ab15c5-af99-41b8-a103-effb69046329", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, nan, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, inf]]]]\n", + "Y = \n", + "[[[[5.17326689,5.17326689,4.88280201,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088,8.06596088],\n", + " [6.38371611,6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.08866644],\n", + " [6.35535574,6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981,5.06112671],\n", + " [5.09879446,5.09879446,4.97382259,4.97382259,4.97382259,3.19434452, inf, inf],\n", + " [3.92282796,3.92282796,4.97382259,4.97382259,4.97382259,2.43473148, inf, inf]]]]\n", + "Indices = \n", + "[[[[ 8, 8,11,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,11,13,13,15,15],\n", + " [17,17,17,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,31],\n", + " [33,33,33,36,36,36,37,38],\n", + " [40,40,51,51,51,45,63,63],\n", + " [48,48,51,51,51,52,63,63]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 3.15168381, 0.39876819, 4.37934923, 3.15053725, 2.68933630, 2.04979014, 2.22631168, 2.91909194],\n", + " [ 5.17326689, 1.48754096, 3.88272882, 4.88280201, 3.82933879, 6.27754688, 4.06093025, 8.06596088],\n", + " [ 3.78974557, 6.38371611,-2.17753601,-3.33859420, 1.70501232, 3.22134590,-4.71752310, 1.27690399],\n", + " [ 5.15344858, 0.95474976, 3.61153436, 1.37445402, 2.46553946,-0.53501892, 4.85525370, 5.08866644],\n", + " [ 3.31550527, 6.35535574, 3.82945824, 5.74075651, 7.07525110, 5.12485981, 5.06112671, 1.61803043],\n", + " [ 5.09879446, 4.01784611, 3.13594031, 4.23551035, 2.28690696, 3.19434452, 2.55583477, 1.47128808],\n", + " [ 3.92282796,-0.15807763, 2.35843778, 4.97382259, 2.43473148, 1.18582726, nan, 0.53569287],\n", + " [ 0.42778468, 2.58801150, 3.42065358, 4.70146227,-1.65992630, 1.79832602, 1.84941924, inf]]]]\n", + "MaxPool: Y = \n", + "[[[[6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,4.88280201,6.27754688,6.27754688,8.06596088],\n", + " [6.38371611,6.38371611,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [6.35535574,6.35535574,7.07525110,7.07525110,7.07525110,5.12485981],\n", + " [5.09879446,4.97382259,4.97382259,4.97382259,3.19434452, inf]]]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-02-02 15:27:49.858876465 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Y' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n", + "\u001b[0;93m2026-02-02 15:27:49.858892965 [W:onnxruntime:, graph.cc:117 MergeShapeInfo] Error merging shape info for output. 'Indices' source:{1,1,8,8} target:{1,1,6,6}. Falling back to lenient merge.\u001b[m\n" + ] + } + ], + "source": [ + "# Case NN4:BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "data = [[[[ 3.15168382, 0.39876820, 4.37934912, 3.15053735, 2.68933629, 2.04979010, 2.22631180, 2.91909200],\n", + " [ 5.17326681, 1.48754092, 3.88272884, 4.88280180, 3.82933887, 6.27754705, 4.06093024, 8.06596070],\n", + " [ 3.78974562, 6.38371591,-2.17753597,-3.33859408, 1.70501236, 3.22134587,-4.71752299, 1.27690398],\n", + " [ 5.15344877, 0.95474975, 3.61153436, 1.37445399, 2.46553948,-0.53501892, 4.85525366, 5.08866624],\n", + " [ 3.31550533, 6.35535590, 3.82945834, 5.74075666, 7.07525101, 5.12485980, 5.06112666, 1.61803044],\n", + " [ 5.09879424, 4.01784619, 3.13594030, 4.23551037, 2.28690697, 3.19434453, 2.55583487, 1.47128808],\n", + " [ 3.92282805,-0.15807763, 2.35843771, 4.97382280, 2.43473158, 1.18582722, np.nan, 0.53569289],\n", + " [ 0.42778469, 2.58801158, 3.42065351, 4.70146247,-1.65992634, 1.79832601, 1.84941921, np.inf]]]]\n", + "x = np.array(data, dtype=np.float32)\n", + "session = create_maxpool_model(x.shape, [1, 1, 6, 6], onnx_type)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, [3,3], [1,1], [1,1], [0,0,0,0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24f67c30-cc60-4175-840c-6745c5a45f15", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem_small_examples.ipynb b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem_small_examples.ipynb new file mode 100644 index 00000000..09290bc5 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/maxpool/work/maxpool_with_python_implem_small_examples.ipynb @@ -0,0 +1,1170 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "02a475ca", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (6.33.0)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.9.23)\n", + "Requirement already satisfied: packaging in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/souyris/RECHERCHE/IA/ONNX/SONNX/sonnx_venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import math\n", + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "from onnx import TensorProto\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "maxpool_output_name1 = \"Y\"\n", + "maxpool_output_name2 = \"Indices\"\n", + "\n", + "\n", + "# Create the ONNX model with maxpool operator\n", + "def create_maxpool_model(input_shape, output_shape, dtype, dilation, paddings, k_shape, store_order=0):\n", + "\n", + " #Create \"input-rank\" input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_shape)\n", + "\n", + " # Create output tensor (final result after maxpool operation)\n", + " maxpool_output1 = onnx.helper.make_tensor_value_info(maxpool_output_name1, dtype, output_shape)\n", + " maxpool_output2 = onnx.helper.make_tensor_value_info(maxpool_output_name2, TensorProto.INT64, output_shape)\n", + "\n", + " # Define MaxPool node\n", + " maxpool_node = onnx.helper.make_node(\n", + " \"MaxPool\",\n", + " inputs=[input1_name],\n", + " outputs=[maxpool_output_name1, maxpool_output_name2],\n", + " ceil_mode = 0,\n", + " dilations=dilation,\n", + " kernel_shape=k_shape,\n", + " pads=paddings,\n", + " strides=[1, 1],\n", + " auto_pad='NOTSET',\n", + " storage_order=store_order\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [maxpool_node],\n", + " \"Test_MaxPool\",\n", + " [input1],\n", + " [maxpool_output1, maxpool_output2],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)]) # Explicitly set opset to 13\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"maxpool.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"maxpool.onnx\")\n", + " return session\n", + "\n", + "def do_maxpool(x, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " indices_f = (np.array2string(output[1], separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + "\n", + " # Display results\n", + " print(f\"X = \\n{x_f}\")\n", + " print(f\"Y = \\n{y_f}\")\n", + " print(f\"Indices = \\n{indices_f}\")\n", + "\n", + "def compute_MaxPool_output_shape(input_shape, kernel_shape, pads, dilation, strides):\n", + "\n", + " dx0, dx1, dx2, dx3 = input_shape\n", + " dw0, dw1 = kernel_shape\n", + "\n", + "\n", + " myalpha = dx2 + pads[0] + pads[2]\n", + " mybeta = dx3 + pads[1] + pads[3]\n", + " mytheta = (dilation[0] * (dw0 - 1)) + 1\n", + " mygamma = (dilation[1] * (dw1 - 1)) + 1\n", + "# dY_2 = \\left\\lfloor{(dX_2 + pad\\_shape[0] - dilations[0] * (kernel\\_shape[0] - 1) - 1) / (strides[0] + 1)}\\right\\rfloor\n", + " mydy2 = math.floor(((myalpha - (mytheta)) / strides[0]) + 1)\n", + "# dY_3 = \\left\\lfloor{(dX_3 + pad\\_shape[1] - dilations[1] * (kernel\\_shape[1] - 1) - 1) / (strides[1] + 1)} \\right\\rfloor\n", + " mydy3 = math.floor(((mybeta - (mygamma)) / strides[1]) + 1)\n", + " \n", + " output_shape = [dx0, dx1, mydy2, mydy3]\n", + "\n", + " return output_shape\n", + "\n", + "\n", + "def pad_4d_spatial_js(tensor, paddings):\n", + " \"\"\"\n", + " Pads the last two dimensions (H, W) of a 4D tensor.\n", + " paddings = (top, left, bottom, right)\n", + " \"\"\"\n", + " N, C, H, W = tensor.shape\n", + " top, left, bottom, right = paddings\n", + " \n", + " # 1. Calculate the new spatial dimensions\n", + " new_H = H + top + bottom\n", + " new_W = W + left + right\n", + " \n", + " # 2. Create the new 4D \"canvas\" of zeros\n", + " # The Batch (N) and Channel (C) dimensions remain the same\n", + "# padded_tensor = np.zeros((N, C, new_H, new_W), dtype=tensor.dtype)\n", + " padded_tensor = np.full((N, C, new_H, new_W), -np.inf)\n", + " \n", + " # 3. Insert the original tensor into the padded one\n", + " # We use \":\" for N and C to select all batches and channels\n", + " # We use slicing for the H and W dimensions\n", + " padded_tensor[:, :, top : top + H, left : left + W] = tensor\n", + " \n", + " return padded_tensor\n", + "\n", + "def extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilations, dW):\n", + " \"\"\"\n", + " Extracts the maximum value from a dilated window.\n", + " \n", + " Args:\n", + " X_p: The 2D padded input array.\n", + " m, n: Integer constants representing the current output position.\n", + " strides: Tuple (s_h, s_w) for step size.\n", + " dilations: Tuple (d_h, d_w) for spacing between window elements.\n", + " dW: Tuple (dW0, dW1) for the window height and width.\n", + " \"\"\"\n", + " dW0, dW1 = dW\n", + " s_h, s_w = strides\n", + " d_h, d_w = dilations\n", + " \n", + " # Calculate the base (top-left) coordinates based on the stride\n", + " base_h = m * s_h\n", + " base_w = n * s_w\n", + " \n", + " # Initialize max_val with the first element of the window (h=0, w=0)\n", + " max_val = X_p[b, c, base_h, base_w]\n", + " \n", + " # Iterate through the window defined by dW0 and dW1\n", + " for h in range(dW0):\n", + " for w in range(dW1):\n", + " # Calculate the current dilated coordinates\n", + " curr_h = base_h + h * d_h\n", + " curr_w = base_w + w * d_w\n", + " \n", + " # Extract the value at these coordinates\n", + " val = X_p[b, c, curr_h, curr_w]\n", + " \n", + " # Update the maximum found so far\n", + " if val > max_val:\n", + " max_val = val\n", + " \n", + " return max_val\n", + "\n", + "def MaxPool(X, kernel_shape, strides, dilation, pads):\n", + "\n", + " Y_dims = compute_MaxPool_output_shape(X.shape, kernel_shape, pads, dilation, strides)\n", + "\n", + " Y = np.zeros(Y_dims)\n", + "\n", + " X_p = pad_4d_spatial_js(X, pads)\n", + " dX_p0, dX_p1, dX_p2, dX_p3 = X_p.shape\n", + " dY_p0, dY_p1, dY_p2, dY_p3 = Y.shape\n", + "\n", + " xp_f = (np.array2string(X_p, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " print(f\"MaxPool: X_p = \\n{xp_f}\")\n", + "\n", + " for b in range(dX_p0):\n", + " for c in range(dX_p1): \n", + " for m in range(dY_p2):\n", + " for n in range(dY_p3):\n", + " Y[b, c, m, n] = extract_max_dilated_from_dilated_window(X_p, b, c, m, n, strides, dilation, kernel_shape)\n", + "\n", + " Y_f = (np.array2string(Y, separator=',', max_line_width=np.inf).replace('\\n', '\\n'))\n", + " print(f\"MaxPool: Y = \\n{Y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 2.14485954, 3.60685315, 5.43428613],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "Y = \n", + "[[[[3.60685315,5.43428613],\n", + " [4.82017601,4.82017601]]]]\n", + "Indices = \n", + "[[[[1,2],\n", + " [7,7]]]]\n", + "MaxPool: X_p = \n", + "[[[[ 2.14485954, 3.60685315, 5.43428613],\n", + " [ 0.24944269, 0.25525759, 1.95981565],\n", + " [-1.57949968, 4.82017601, 3.77251085]]]]\n", + "MaxPool: Y = \n", + "[[[[3.60685315,5.43428613],\n", + " [4.82017601,4.82017601]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 3, 3))\n", + "y_shape=[1, 1, 2, 2]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "dilation=[1,1]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "49d704e3-14ac-428a-a2f4-79211a899530", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ -inf, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[-1.79769313e+308, 4.56432533e+000],\n", + " [ 3.46789489e+000, 5.23979851e+000]]]]\n", + "Indices = \n", + "[[[[-4, 2],\n", + " [ 7, 8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "MaxPool: Y = \n", + "[[[[ -inf,4.56432533],\n", + " [3.46789489,5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.inf,-np.inf,4.56432533],\n", + " [-np.inf,-np.inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "866fdbab-0c9a-4308-b9ce-bb0aae605c69", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ nan, 3.10000000, 4.56432533],\n", + " [ 12.81000000,-81.12000000, 2.55354471],\n", + " [ 2.83691720, 3.46789489, 5.23979851]]]]\n", + "Y = \n", + "[[[[12.81000000, 4.56432533],\n", + " [12.81000000, 5.23979851]]]]\n", + "Indices = \n", + "[[[[3,2],\n", + " [3,8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ nan, 3.10000000, 4.56432533],\n", + " [ 12.81000000,-81.12000000, 2.55354471],\n", + " [ 2.83691720, 3.46789489, 5.23979851]]]]\n", + "MaxPool: Y = \n", + "[[[[ nan, 4.56432533],\n", + " [12.81000000, 5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.nan,3.1,4.56432533],\n", + " [12.81,-81.12,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "dc72c6aa-5ba2-4847-bd6d-f454ad47faa2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ nan, nan,4.56432533],\n", + " [ nan, nan,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[-1.79769313e+308, 4.56432533e+000],\n", + " [ 3.46789489e+000, 5.23979851e+000]]]]\n", + "Indices = \n", + "[[[[-4, 2],\n", + " [ 7, 8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ nan, nan,4.56432533],\n", + " [ nan, nan,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "MaxPool: Y = \n", + "[[[[nan,nan],\n", + " [nan,nan]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.nan,-np.nan,4.56432533],\n", + " [-np.nan,-np.nan,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9f30b763-66f0-4f40-8bf3-93ae93bcd7b2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ nan, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[-1.79769313e+308, 4.56432533e+000],\n", + " [ 3.46789489e+000, 5.23979851e+000]]]]\n", + "Indices = \n", + "[[[[-4, 2],\n", + " [ 7, 8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ nan, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "MaxPool: Y = \n", + "[[[[ nan,4.56432533],\n", + " [3.46789489,5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.nan,-np.inf,4.56432533],\n", + " [-np.inf,-np.inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "252d61c3-d97f-44c9-85e2-d9160f6950ba", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ -inf, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[-1.79769313e+308, 4.56432533e+000],\n", + " [ 3.46789489e+000, 5.23979851e+000]]]]\n", + "Indices = \n", + "[[[[-4, 2],\n", + " [ 7, 8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf,4.56432533],\n", + " [ -inf, -inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "MaxPool: Y = \n", + "[[[[ -inf,4.56432533],\n", + " [3.46789489,5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.inf,-np.inf,4.56432533],\n", + " [-np.inf,-np.inf,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "135748cb-b557-4302-84f2-d94e5f9e9ca4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 7.75370483, 5.26279449, 4.16173896],\n", + " [-1.86658564, 0.19780145, 0.87829406],\n", + " [ 4.80944867, 3.42518172, 7.53025056]]]]\n", + "Y = \n", + "[[[[7.75370483,5.26279449],\n", + " [7.75370483,5.26279449],\n", + " [4.80944867,7.53025056],\n", + " [4.80944867,7.53025056]]]]\n", + "Indices = \n", + "[[[[0,1],\n", + " [0,1],\n", + " [6,8],\n", + " [6,8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf, -inf],\n", + " [ 7.75370483, 5.26279449, 4.16173896],\n", + " [-1.86658564, 0.19780145, 0.87829406],\n", + " [ 4.80944867, 3.42518172, 7.53025056],\n", + " [ -inf, -inf, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[7.75370483,5.26279449],\n", + " [7.75370483,5.26279449],\n", + " [4.80944867,7.53025056],\n", + " [4.80944867,7.53025056]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 3, 3))\n", + "y_shape=[1, 1, 4, 2]\n", + "dilation=[1,1]\n", + "pads = [1,0,1,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "7e3ba978-dacf-46e3-9f77-8f7c908d1582", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ -inf,9.57875561,4.56432533],\n", + " [2.72844928,3.54234851,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[-1.79769313e+308, 9.57875561e+000, 9.57875561e+000, 4.56432533e+000],\n", + " [ 2.72844928e+000, 9.57875561e+000, 9.57875561e+000, 4.56432533e+000],\n", + " [ 2.83691720e+000, 3.54234851e+000, 5.23979851e+000, 5.23979851e+000],\n", + " [ 2.83691720e+000, 3.46789489e+000, 5.23979851e+000, 5.23979851e+000]]]]\n", + "Indices = \n", + "[[[[-4, 1, 1, 2],\n", + " [ 3, 1, 1, 2],\n", + " [ 6, 4, 8, 8],\n", + " [ 6, 7, 8, 8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf, -inf, -inf, -inf],\n", + " [ -inf, -inf,9.57875561,4.56432533, -inf],\n", + " [ -inf,2.72844928,3.54234851,2.55354471, -inf],\n", + " [ -inf,2.83691720,3.46789489,5.23979851, -inf],\n", + " [ -inf, -inf, -inf, -inf, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[ -inf,9.57875561,9.57875561,4.56432533],\n", + " [2.72844928,9.57875561,9.57875561,4.56432533],\n", + " [2.83691720,3.54234851,5.23979851,5.23979851],\n", + " [2.83691720,3.46789489,5.23979851,5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.inf,9.57875561,4.56432533],\n", + " [2.72844928,3.54234851,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 4, 4]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "334307f6-0318-4be2-8b13-ff044b314715", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ -inf,9.57875561,4.56432533],\n", + " [2.72844928,3.54234851,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "Y = \n", + "[[[[2.72844928,9.57875561,9.57875561,4.56432533],\n", + " [2.83691720,3.54234851,5.23979851,5.23979851]]]]\n", + "Indices = \n", + "[[[[3,1,1,2],\n", + " [6,4,8,8]]]]\n", + "MaxPool: X_p = \n", + "[[[[ -inf, -inf,9.57875561,4.56432533, -inf],\n", + " [ -inf,2.72844928,3.54234851,2.55354471, -inf],\n", + " [ -inf,2.83691720,3.46789489,5.23979851, -inf]]]]\n", + "MaxPool: Y = \n", + "[[[[2.72844928,9.57875561,9.57875561,4.56432533],\n", + " [2.83691720,3.54234851,5.23979851,5.23979851]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.DOUBLE\n", + "data = [[[[-np.inf,9.57875561,4.56432533],\n", + " [2.72844928,3.54234851,2.55354471],\n", + " [2.83691720,3.46789489,5.23979851]]]]\n", + "x = np.array(data, dtype=np.float64)\n", + "y_shape=[1, 1, 2, 4]\n", + "dilation=[1,1]\n", + "pads = [0,1,0,1]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "MaxPool(x, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "bd658b06-0501-44c4-8123-8a36573527b9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[3,5,2],\n", + " [4,5,3],\n", + " [4,4,3]]]]\n", + "Y = \n", + "[[[[3,5,5],\n", + " [4,5,5],\n", + " [4,5,5]]]]\n", + "Indices = \n", + "[[[[0,1,1],\n", + " [3,1,1],\n", + " [3,4,4]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.INT8\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 3, 3))\n", + "x_int8 = np.clip(np.round(x), -128, 127).astype(np.int8)\n", + "y_shape=[1, 1, 3, 3]\n", + "pads = [1,1,0,0]\n", + "kernel_shape = [2,2]\n", + "dilation=[1,1]\n", + "session = create_maxpool_model(x_int8.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x_int8, session)\n", + "#MaxPool(x_int8, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "5ccc411d-cf0e-4518-99b0-526b144b97e3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-12,-13, 5],\n", + " [-14,-15, 6],\n", + " [ 7, 8, -1]]]]\n", + "Y = \n", + "[[[[-12, 6],\n", + " [ 8, 8]]]]\n", + "Indices = \n", + "[[[[0,5],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ -12, -13, 5],\n", + " [ -14, -15, 6],\n", + " [ 7, 8, -1]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "144d8548-92e0-4b3d-8730-70d414829614", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-128,-128, 5],\n", + " [-128,-128, 6],\n", + " [ 7, 8, -1]]]]\n", + "Y = \n", + "[[[[-128, 6],\n", + " [ 8, 8]]]]\n", + "Indices = \n", + "[[[[-4, 5],\n", + " [ 7, 7]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ -128, -128, 5],\n", + " [ -128, -128, 6],\n", + " [ 7, 8, -1]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a8ef9d2c-405f-4982-aee5-0fb469c62f57", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 6, 2,-1],\n", + " [ 3, 2, 0],\n", + " [ 1, 3,-2]]]]\n", + "Y = \n", + "[[[[ 6, 6, 2, 0],\n", + " [ 3, 3, 3, 0],\n", + " [ 1, 3, 3,-2]]]]\n", + "Indices = \n", + "[[[[0,0,1,5],\n", + " [3,3,7,5],\n", + " [6,7,7,8]]]]\n" + ] + } + ], + "source": [ + "# Case N1: BLA BLA BLA\n", + "onnx_type = onnx.TensorProto.INT8\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 3, 3))\n", + "x_int8 = np.clip(np.round(x), -128, 127).astype(np.int8)\n", + "y_shape=[1, 1, 3, 4]\n", + "dilation=[1,1]\n", + "pads = [0,1,1,1]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x_int8.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x_int8, session)\n", + "#MaxPool(x_int8, kernel_shape, [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "fd336a06-c5cc-420f-82d2-7f77c69f0ba6", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[-128,-127, 5],\n", + " [-128,-127, 6],\n", + " [ 7, 8,-128]]]]\n", + "Y = \n", + "[[[[-128,-127, 5, 5],\n", + " [-128,-127, 6, 6],\n", + " [ 7, 8, 8, 6],\n", + " [ 7, 8, 8,-128]]]]\n", + "Indices = \n", + "[[[[-4, 1, 2, 2],\n", + " [-4, 1, 5, 5],\n", + " [ 6, 7, 7, 5],\n", + " [ 6, 7, 7,-4]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.INT8\n", + "data = [[[[ -128, -127, 5],\n", + " [ -128, -127, 6],\n", + " [ 7, 8, -128]]]]\n", + "x = np.array(data, dtype=np.int8)\n", + "y_shape=[1, 1, 4, 4]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "33dabc91-5db2-4f1e-8f24-cbb62c10621b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[3,3,3],\n", + " [3,5,0],\n", + " [6,2,1]]]]\n", + "Y = \n", + "[[[[5,3],\n", + " [3,6]]]]\n", + "Indices = \n", + "[[[[4,3],\n", + " [1,6]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "x = np.random.normal(3, 2.5, size=(1, 1, 3, 3))\n", + "x_uint8 = np.clip(np.round(x), 0, 127).astype(np.uint8)\n", + "y_shape=[1, 1, 2, 2]\n", + "pads = [1,1,0,0]\n", + "kernel_shape = [2,2]\n", + "dilation=[2,2]\n", + "session = create_maxpool_model(x_uint8.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x_uint8, session)\n", + "#MaxPool(x_int8, kernel_shape, [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "495abd56-b31f-4e06-a609-754290fd36bc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[3,0,4],\n", + " [3,5,6],\n", + " [8,1,4]]]]\n", + "Y = \n", + "[[[[5,6],\n", + " [1,8]]]]\n", + "Indices = \n", + "[[[[4,5],\n", + " [7,6]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "data = [[[[ 3, 0, 4],\n", + " [ 3, 5, 6],\n", + " [ 8, 1, 4]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 2, 2]\n", + "pads = [1,1,0,0]\n", + "kernel_shape = [2,2]\n", + "dilation=[2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], [1,1], pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "2aab6c7d-62ef-44ed-a529-d14a53258b5e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[0,1,5],\n", + " [1,1,6],\n", + " [7,8,0]]]]\n", + "Y = \n", + "[[[[0,1,5,5],\n", + " [1,1,6,6],\n", + " [7,8,8,6],\n", + " [7,8,8,0]]]]\n", + "Indices = \n", + "[[[[-4, 1, 2, 2],\n", + " [ 3, 1, 5, 5],\n", + " [ 6, 7, 7, 5],\n", + " [ 6, 7, 7,-4]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "data = [[[[ 0, 1, 5],\n", + " [ 1, 1, 6],\n", + " [ 7, 8, 0]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 4, 4]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "c377fb67-a492-48fa-922f-1fa4651599bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[0,1,5],\n", + " [1,1,6],\n", + " [7,8,0]]]]\n", + "Y = \n", + "[[[[0,1,5,5],\n", + " [1,1,6,6],\n", + " [7,8,8,6],\n", + " [7,8,8,0]]]]\n", + "Indices = \n", + "[[[[-4, 3, 6, 6],\n", + " [ 1, 3, 7, 7],\n", + " [ 2, 5, 5, 7],\n", + " [ 2, 5, 5,-4]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "data = [[[[ 0, 1, 5],\n", + " [ 1, 1, 6],\n", + " [ 7, 8, 0]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 4, 4]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "storage_order = 1\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape, storage_order)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "c2919e73-8cb8-4dc5-925d-0e121c320b5b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 0, 1, 5,12],\n", + " [ 1, 1, 6, 2],\n", + " [ 7, 8, 9,10],\n", + " [ 7, 8, 0, 0]]]]\n", + "Y = \n", + "[[[[ 0, 1, 5,12,12],\n", + " [ 1, 1, 6,12,12],\n", + " [ 7, 8, 9,10,10],\n", + " [ 7, 8, 9,10,10],\n", + " [ 7, 8, 8, 0, 0]]]]\n", + "Indices = \n", + "[[[[-5, 4, 8,12,12],\n", + " [ 1, 4, 9,12,12],\n", + " [ 2, 6,10,14,14],\n", + " [ 2, 6,10,14,14],\n", + " [ 3, 7, 7,-5,-5]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "data = [[[[ 0, 1, 5, 12],\n", + " [ 1, 1, 6, 2],\n", + " [ 7, 8, 9, 10],\n", + " [ 7, 8, 0, 0]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 5, 5]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "storage_order = 1\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape, storage_order)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "774d2d3e-fda8-40b5-a543-8d24773dfe0f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[ 0, 1, 5, 12, 13],\n", + " [ 1, 1, 6, 2, 22],\n", + " [ 7, 8, 9, 10,100],\n", + " [ 7, 8, 9, 10,123],\n", + " [ 7, 8, 0, 0, 0]]]]\n", + "Y = \n", + "[[[[ 0, 1, 5, 12, 13, 13],\n", + " [ 1, 1, 6, 12, 22, 22],\n", + " [ 7, 8, 9, 10,100,100],\n", + " [ 7, 8, 9, 10,123,123],\n", + " [ 7, 8, 9, 10,123,123],\n", + " [ 7, 8, 8, 0, 0, 0]]]]\n", + "Indices = \n", + "[[[[-6, 5,10,15,20,20],\n", + " [ 1, 5,11,15,21,21],\n", + " [ 2, 7,12,17,22,22],\n", + " [ 2, 7,12,17,23,23],\n", + " [ 3, 8,13,18,23,23],\n", + " [ 4, 9, 9,-6,-6,-6]]]]\n" + ] + } + ], + "source": [ + "onnx_type = onnx.TensorProto.UINT8\n", + "data = [[[[ 0, 1, 5, 12, 13],\n", + " [ 1, 1, 6, 2, 22],\n", + " [ 7, 8, 9, 10, 100],\n", + " [ 7, 8, 9, 10, 123],\n", + " [ 7, 8, 0, 0, 0]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 6, 6]\n", + "dilation=[1,1]\n", + "pads = [1,1,1,1]\n", + "kernel_shape = [2,2]\n", + "storage_order = 1\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape, storage_order)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "9e5e37c7-9ad3-4aae-a37e-1d5f49432f2d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X = \n", + "[[[[0,1,5],\n", + " [1,1,6],\n", + " [7,8,0]]]]\n", + "Y = \n", + "[[[[1,6],\n", + " [8,8]]]]\n", + "Indices = \n", + "[[[[1,5],\n", + " [7,7]]]]\n" + ] + } + ], + "source": [ + "data = [[[[ 0, 1, 5],\n", + " [ 1, 1, 6],\n", + " [ 7, 8, 0]]]]\n", + "x = np.array(data, dtype=np.uint8)\n", + "y_shape=[1, 1, 2, 2]\n", + "dilation=[1,1]\n", + "pads = [0,0,0,0]\n", + "kernel_shape = [2,2]\n", + "session = create_maxpool_model(x.shape, y_shape, onnx_type, dilation, pads, kernel_shape)\n", + "do_maxpool(x, session)\n", + "#MaxPool(x_int8, [3,3], [1,1], dilation, pads)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63ba4759-5ce4-41cc-8d50-3e387e941a92", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.8" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/README.md b/safety-related-profile/sonnx/ops/spec/tests/mul/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/67b0a8ccf18bf5d2 b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/67b0a8ccf18bf5d2 new file mode 100644 index 00000000..d5ab4daa --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/67b0a8ccf18bf5d2 @@ -0,0 +1,4 @@ +# file: /usr/lib/python3.12/sitecustomize.py +# hypothesis_version: 6.151.9 + +[] \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/9dc351f8eb528bfa b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/9dc351f8eb528bfa new file mode 100644 index 00000000..6c4b000c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/.hypothesis/constants/9dc351f8eb528bfa @@ -0,0 +1,4 @@ +# file: /home/mariem/aidge/env_aidge/bin/pytest +# hypothesis_version: 6.151.9 + +['.exe', '__main__'] \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/__pycache__/test_mul.cpython-312-pytest-9.0.1.pyc b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/__pycache__/test_mul.cpython-312-pytest-9.0.1.pyc new file mode 100644 index 00000000..54ce2719 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/__pycache__/test_mul.cpython-312-pytest-9.0.1.pyc differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/test_mul.py b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/test_mul.py new file mode 100644 index 00000000..3137afe6 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/test_mul.py @@ -0,0 +1,323 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Mul operation in ONNX. +""" +import os +import json +import numpy as np +try: + import ml_dtypes # kept for consistency with test_clip.py, NOT REALLY USED. +except ImportError: + ml_dtypes = None + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + +try: + import tensorflow as tf # kept for consistency with test_clip.py, NOT REALLY USED. +except ModuleNotFoundError: + tf = None + +from hypothesis import given, settings, assume +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + + +""" +Clean previous generated data +""" +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Inputs/attributes details +""" +inputs_attributes = { + "min_shape_size_input": 0, # number of dimensions (rank) + "max_shape_size_input": 4, # number of dimensions (rank) + "min_size_input_axis": 1, # no zero dimension + "max_size_input_axis": 10, + "ONNXRuntime_Provider": "CPUExecutionProvider" # CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + + +""" +Mul supported types (adapt if it restricts Mul differently) +""" +mul_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT32": np.int32, + "UINT8": np.uint8, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64 + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + } +} + + +""" +Store/log generated data +""" +generated_data = { + "shape_size_input_a": [], + "shape_size_input_b": [], + "shape_input_a": [], + "shape_input_b": [], + "a_tensor": [], + "b_tensor": [] +} + + +""" +Helper: build a broadcast-compatible pair of shapes +We generate a "result shape" then derive a_shape and b_shape from it. +""" +def _make_broadcastable_shapes(draw): + # result rank + y_rank = draw(st.integers( + min_value=inputs_attributes["min_shape_size_input"], + max_value=inputs_attributes["max_shape_size_input"] + )) + + # result shape (each dim >= 1) + y_shape = [] + for _ in range(y_rank): + d = draw(st.integers( + min_value=inputs_attributes["min_size_input_axis"], + max_value=inputs_attributes["max_size_input_axis"] + )) + y_shape.append(d) + + # choose ranks for a and b (<= y_rank) + a_rank = draw(st.integers(min_value=0, max_value=y_rank)) + b_rank = draw(st.integers(min_value=0, max_value=y_rank)) + + # align right --> pick the last a_rank/b_rank dims from y_shape + y_tail_for_a = y_shape[-a_rank:] if a_rank > 0 else [] + y_tail_for_b = y_shape[-b_rank:] if b_rank > 0 else [] + + # for each dim in the tail, choose either 1 or the corresponding y dim + a_shape = [] + for d in y_tail_for_a: + a_shape.append(draw(st.sampled_from([1, d]))) + + b_shape = [] + for d in y_tail_for_b: + b_shape.append(draw(st.sampled_from([1, d]))) + + # Ensure broadcast is valid (sanity check with numpy) + a_dummy = np.empty(a_shape, dtype=np.float32) + b_dummy = np.empty(b_shape, dtype=np.float32) + broadcasted_shape = np.broadcast(a_dummy, b_dummy).shape # must not throw + + return list(a_shape), list(b_shape), list(broadcasted_shape) + + +""" +Function to generate valid Mul arguments +""" + +@st.composite +def valid_mul_args(draw): + # Restrictions : type consistency + all_valid_types = list(mul_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + dtype_name = draw(st.sampled_from(all_valid_types)) + dtype = mul_types[inputs_attributes["ONNXRuntime_Provider"]][dtype_name] + + # Choose numeric elements strategy + if np.issubdtype(dtype, np.integer): + # Keep values moderate to avoid "too wild" overflows in some backends. + # (Still valid for Mul semantics; adjust if you want full range.) + info = np.iinfo(dtype) + lo = max(info.min, -10_000) if np.issubdtype(dtype, np.signedinteger) else 0 + hi = min(info.max, 10_000) + a_numeric = st.integers(min_value=lo, max_value=hi) + elif np.issubdtype(dtype, np.floating): + # allow NaN and +/-Inf: MUST NOT set min_value/max_value in Hypothesis + # width controls the kinds of floats generated + width = 16 if dtype == np.float16 else 32 if dtype == np.float32 else 64 + a_numeric = st.floats(allow_nan=True, allow_infinity=True, width=width) + else: + # If your Mul supports other types, add them here + assume(False) + + + # Input shapes : broadcast-compatible + a_shape, b_shape, y_shape = _make_broadcastable_shapes(draw) + + # Input tensors + a = draw(hnp.arrays(dtype=dtype, shape=a_shape, elements=a_numeric)) + b = draw(hnp.arrays(dtype=dtype, shape=b_shape, elements=a_numeric)) + + return (a_shape, b_shape, a, b, dtype_name, y_shape) + + +""" +Test function +""" +failed_cases = [] +warning_cases = [] # Alertes mathématiques (Overflow, NaN) + +@settings(max_examples=2000, deadline=None) +@given(valid_mul_args()) +def test_mul(args): + a_shape, b_shape, a, b, dtype_name, y_shape = args + + # On demande à NumPy de lever une erreur pour les overflows et les calculs invalides + with np.errstate(over='raise', invalid='raise'): + try: + # 1. Calcul ONNX + y = run_onnx_mul(a_shape, b_shape, a, b, dtype_name, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + + # 2. Vérification (Ici les warnings NumPy seront levés) + check_constraints(a, b, y) + + except (FloatingPointError, RuntimeWarning) as w: + # CAS DES WARNINGS (Overflow / Invalid) + # On enregistre + if len(warning_cases) < 500: + warning_cases.append({ + "type": "Warning Mathématique", + "detail": str(w), + "dtype": dtype_name, + "tensor_a": a.tolist(), + "tensor_b": b.tolist() + }) + + except AssertionError as e: + # CAS DES ERREURS (ONNX donne un mauvais résultat) + failed_cases.append({ + "type": "Erreur d'Assertion", + "detail": str(e), + "dtype": dtype_name, + "shape_a": a_shape, + "shape_b": b_shape, + "tensor_a": a.tolist(), + "tensor_b": b.tolist() + }) + raise e + + +def teardown_module(): + # --- Fichier 1 : Les erreurs réelles --- + if failed_cases: + with open("failures_report.json", "w", encoding="utf-8") as f: + json.dump({"total_errors": len(failed_cases), "errors": failed_cases}, f, indent=4) + print(f"\n[!!!] {len(failed_cases)} ERREURS enregistrées dans failures_report.json") + + # --- Fichier 2 : Les warnings mathématiques --- + if warning_cases: + with open("warnings_report.json", "w", encoding="utf-8") as f: + json.dump({"total_warnings": len(warning_cases), "warnings": warning_cases}, f, indent=4) + print(f"\n[i] {len(warning_cases)} WARNINGS enregistrés dans warnings_report.json") + +""" +Function that runs the ONNX Mul operation +""" +def run_onnx_mul(a_shape, b_shape, a, b, dtype_name, y_shape, provider): + # Create inputs + a_onnx = helper.make_tensor_value_info( + 'a', + helper.np_dtype_to_tensor_dtype(a.dtype), + list(a_shape) + ) + b_onnx = helper.make_tensor_value_info( + 'b', + helper.np_dtype_to_tensor_dtype(b.dtype), + list(b_shape) + ) + + node_def = helper.make_node( + 'Mul', + ['a', 'b'], + ['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-mul', + [a_onnx, b_onnx], + [helper.make_tensor_value_info( + 'y', + helper.np_dtype_to_tensor_dtype(a.dtype), + list(y_shape) + )], + ) + + onnx_model = helper.make_model(graph_def) + + # Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 14 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + sess = InferenceSession( + onnx_model.SerializeToString(), + providers=[provider] + ) + + y = sess.run(None, {'a': a, 'b': b})[0] + return y + + +def check_constraints(a, b, y): + """ + Function that defines asserts for the constraints (Mul) + """ + + # Shape consistency: y must match numpy broadcasting + expected_shape = np.broadcast(a, b).shape + assert y.shape == expected_shape + + # Type consistency + assert a.dtype == b.dtype == y.dtype + + # Value consistency: compare to numpy reference + ref = np.multiply(a, b) + + if np.issubdtype(y.dtype, np.floating): + # tolerate rounding differences especially for float16 + if y.dtype == np.float16: + rtol, atol = 1e-2, 1e-2 + else: + rtol, atol = 1e-5, 1e-6 + + # equal_nan=True is required when we allow NaN generation + np.testing.assert_allclose(y, ref, rtol=rtol, atol=atol, equal_nan=True) + else: + # integers: exact match (numpy uses wrap-around for fixed-width ints) + assert np.array_equal(y, ref) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/warnings_report.json b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/warnings_report.json new file mode 100644 index 00000000..ce40e83d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/mul/hypothesis/warnings_report.json @@ -0,0 +1,7105 @@ +{ + "total_warnings": 43, + "warnings": [ + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + -3.986312162952938e+16, + -2.2250738585072014e-308, + -4.651679641617968e+16 + ], + "tensor_b": [ + [ + [ + [ + -1.000856470785297e+306 + ] + ], + [ + [ + 1.7399042189176515e-91 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ], + [ + [ + -1.7179304277548672e+16 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ], + [ + [ + -6.81168506377127e+16 + ] + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -2.3407904248897104e+209, + "tensor_b": -2.3407904248897104e+209 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -2.3407904248897104e+209, + "tensor_b": -2.3407904248897104e+209 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + -5.933321225764864e+16 + ], + [ + 0.0 + ], + [ + 2.5391026315198464e+16 + ], + [ + 1.4221391788780437e+35 + ], + [ + -5.819456059290419e+16 + ], + [ + 7458818447376384.0 + ] + ], + "tensor_b": [ + -Infinity, + -Infinity, + -Infinity, + NaN + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + [ + -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, + -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, + -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, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + 2.268333075542835e+16, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -7.173117222846464e+16 + ], + [ + -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, + -0.0, + -0.0 + ] + ], + [ + [ + -3.816296197337907e+16, + -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, + 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, + -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, + -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, + 4.884240674481766e+16, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + 5.660473120364954e+16 + ], + [ + -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, + -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, + -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, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -3.926712505073664e+16, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + Infinity, + -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, + -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, + -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, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -6.23819346631721e+16, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -1.401298464324817e-45, + -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, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + 5.720693286318899e+16, + -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, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + 3.4028234663852886e+38, + -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, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -2.220446049250313e-16 + ], + [ + -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, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -1.05918456922112e+16, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + 2.4940860055486464e+16, + -0.0, + -0.0, + -0.0, + -0.0, + 5.862907384430592e+16, + -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, + -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, + -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 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -3.962918649882214e+16, + -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 + ], + [ + -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, + -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, + -0.0, + -4.59987733226455e+16, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + 4.607095883798938e+16, + -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, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + 2.216267763993805e+16 + ], + [ + -0.0, + -0.0, + -0.0, + 1.8414217015066624e+16, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + 5520575405490176.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.0, + -0.0 + ], + [ + -0.0, + -0.0, + -3.4459879825473536e+16, + -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, + -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, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + 0.0, + -0.0, + -0.0, + -4.19021347314729e+16, + -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, + -0.0, + -0.0, + -1.1920928955078125e-07 + ], + [ + -0.0, + -6.3901605683313505e-22, + -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 + ], + [ + -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, + -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, + -0.0, + -0.0, + -0.0, + -0.0 + ], + [ + -0.0, + -0.0, + -0.0, + -0.0, + -0.0, + 3391985587060736.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.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.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.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.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.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.2933325496778752e+16, + -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, + -0.0 + ] + ] + ], + "tensor_b": -2.7232523608129536e+16 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + [ + 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, + 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, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 2.268333075542835e+16, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -7.173117222846464e+16 + ], + [ + 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, + 0.0, + 0.0 + ] + ], + [ + [ + -3.816296197337907e+16, + 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, + 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, + 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, + 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, + 4.884240674481766e+16, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 5.660473120364954e+16 + ], + [ + 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, + 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, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -3.926712505073664e+16, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + Infinity, + 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, + 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, + 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, + 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.401298464324817e-45, + 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, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 5.720693286318899e+16, + 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, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 3.4028234663852886e+38, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -2.220446049250313e-16 + ], + [ + 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, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + -1.05918456922112e+16, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 2.4940860055486464e+16, + 0.0, + 0.0, + 0.0, + 0.0, + 5.862907384430592e+16, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -4.59987733226455e+16, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 4.607095883798938e+16, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 2.216267763993805e+16 + ], + [ + 0.0, + 0.0, + 0.0, + 1.8414217015066624e+16, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + [ + 5520575405490176.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.0, + 0.0 + ], + [ + 0.0, + 0.0, + -3.4459879825473536e+16, + 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, + 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, + 0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + -4.19021347314729e+16, + 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, + 0.0, + 0.0, + -1.1920928955078125e-07 + ], + [ + 0.0, + -6.3901605683313505e-22, + 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 + ], + [ + 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, + 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, + 0.0, + 0.0, + 0.0, + -0.0 + ], + [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 3391985587060736.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.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.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.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.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.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.2933325496778752e+16, + 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, + 0.0 + ] + ] + ], + "tensor_b": -6.23819346631721e+16 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 0.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 0.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ] + ], + "tensor_b": Infinity + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 0.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 0.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ], + [ + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ], + [ + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0, + 3391985587060736.0 + ] + ] + ], + "tensor_b": Infinity + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ] + ], + [ + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 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 + ], + [ + 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, + 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, + 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, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ] + ] + ], + "tensor_b": Infinity + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + -2.465210833227803e-142, + -6.073749838948056e+16, + 4.9257846489702835e+252, + -6.073749838948056e+16, + -6.073749838948056e+16 + ], + [ + -6.073749838948056e+16, + -6.073749838948056e+16, + -6.229832015141402e+16, + -6.073749838948056e+16, + -6.073749838948056e+16 + ], + [ + -6.073749838948056e+16, + -6.073749838948056e+16, + 2.8556620491172856e+16, + -3.1568084615241492e+16, + -6.073749838948056e+16 + ], + [ + -6.073749838948056e+16, + -6.073749838948056e+16, + -1.401298464324817e-45, + -6.073749838948056e+16, + -6.073749838948056e+16 + ] + ], + "tensor_b": [ + [ + 3.326214042957856e+174 + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + -2.465210833227803e-142, + 3.326214042957856e+174, + 4.9257846489702835e+252, + 3.326214042957856e+174, + 3.326214042957856e+174 + ], + [ + 3.326214042957856e+174, + 3.326214042957856e+174, + -6.229832015141402e+16, + 3.326214042957856e+174, + 3.326214042957856e+174 + ], + [ + 3.326214042957856e+174, + 3.326214042957856e+174, + 2.8556620491172856e+16, + -3.1568084615241492e+16, + 3.326214042957856e+174 + ], + [ + 3.326214042957856e+174, + 3.326214042957856e+174, + -1.401298464324817e-45, + 3.326214042957856e+174, + 3.326214042957856e+174 + ] + ], + "tensor_b": [ + [ + 3.326214042957856e+174 + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + -2.0753490280185856e+16 + ], + [ + -1.3075317911823345e+23 + ], + [ + -1.9346053119606784e+16 + ] + ], + "tensor_b": -6.223813486313472e+16 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -3.7246696651210356e+102, + "tensor_b": [ + [ + -8.080503477649841e-275 + ], + [ + 8954210222063363.0 + ], + [ + -4462322707568489.0 + ], + [ + -4462322707568489.0 + ], + [ + -7.283876092167572e+265 + ], + [ + -4462322707568489.0 + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + -0.9999899864196777 + ], + [ + -4.744890890059776e+16 + ], + [ + -2.990705128295629e+16 + ], + [ + -2.687846368411648e+16 + ], + [ + 2.483678655545344e+16 + ], + [ + -0.0 + ], + [ + -4.739731775343821e+16 + ], + [ + 7.053599879410483e+16 + ], + [ + -0.9999899864196777 + ] + ], + "tensor_b": [ + [ + [ + 1.899999976158142 + ] + ], + [ + [ + 4.043839269712691e+16 + ] + ], + [ + [ + 5.072306125458637e+16 + ] + ], + [ + [ + -4588316783542272.0 + ] + ], + [ + [ + -Infinity + ] + ], + [ + [ + -2.836587313574707e+16 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -3.3949327225747304e+16, + "tensor_b": -8.727345509176342e+307 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP64", + "tensor_a": -Infinity, + "tensor_b": 0.0 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + -2.00001 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + -3.371073460339708e+16, + -3.0416641117863416e+16, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + -1.8999562294869628e+16, + 7.874681012602979e+114, + 7.874681012602979e+114, + 1.0068668848487244e+281 + ], + [ + 7.874681012602979e+114, + -9007199254740992.0, + -6.064158639330541e+16, + 1.0048439609704576e+16 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 3.932525604493081e+291, + 9701910224777060.0, + 7.874681012602979e+114, + -1.7976931348623157e+308 + ] + ], + "tensor_b": [ + 3747844977534299.0, + 1.44542242436055e-179, + 9.121442016634821e-172, + -2.00001 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + -2.00001 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + -3.371073460339708e+16, + -3.0416641117863416e+16, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + -1.8999562294869628e+16, + 7.874681012602979e+114, + 7.874681012602979e+114, + 1.0068668848487244e+281 + ], + [ + 7.874681012602979e+114, + -9007199254740992.0, + -6.064158639330541e+16, + 1.0048439609704576e+16 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114, + 7.874681012602979e+114 + ], + [ + 3.932525604493081e+291, + 9701910224777060.0, + 7.874681012602979e+114, + -1.7976931348623157e+308 + ] + ], + "tensor_b": [ + -6.064158639330541e+16, + 0.0, + 0.0, + 0.0 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": 1.3374298759379354e+155, + "tensor_b": 1.3374298759379354e+155 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + 1.175494351e-38, + 1.175494351e-38, + -1.0209155384385713e-174, + 1.175494351e-38, + 1.175494351e-38, + -1.5322652291489065e+178 + ], + "tensor_b": [ + 1.175494351e-38, + 1.175494351e-38, + -1.0209155384385713e-174, + 1.175494351e-38, + 1.175494351e-38, + -1.5322652291489065e+178 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + -1.5322652291489065e+178 + ], + "tensor_b": [ + -1.5322652291489065e+178 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + [ + -6.197174810653491e+16, + 0.9999899864196777, + 2.412656658308783e+29, + -0.0, + -3.640505333396275e+16, + -0.3333333432674408, + -8.554646865010143e-41, + Infinity, + NaN, + -5.960464477539063e-08 + ] + ], + "tensor_b": [ + [ + [ + 0.0, + 0.0, + 1.840931430989824e+16, + 6.367774777121178e+16, + -0.0, + 0.0, + 1.55864094736384e+16, + Infinity, + 0.0, + -6423203584933888.0 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + 0.0 + ], + "tensor_b": [ + -Infinity + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + 0.0 + ], + "tensor_b": [ + -Infinity + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + -Infinity + ], + "tensor_b": [ + 0.0 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": -Infinity, + "tensor_b": 0.0 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + -6.8382765572799656e+16 + ], + [ + NaN + ], + [ + -2.8703455778021874e-155 + ], + [ + 0.0 + ], + [ + -5.725336906712863e+268 + ], + [ + -Infinity + ], + [ + -6.8382765572799656e+16 + ], + [ + -6.8382765572799656e+16 + ] + ], + "tensor_b": [ + [ + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + 0.0 + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ], + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + -6.8382765572799656e+16 + ], + [ + NaN + ], + [ + -2.8703455778021874e-155 + ], + [ + 0.0 + ], + [ + -5.725336906712863e+268 + ], + [ + -Infinity + ], + [ + -6.8382765572799656e+16 + ], + [ + -6.8382765572799656e+16 + ] + ], + "tensor_b": [ + [ + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + 0.0 + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ], + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + -6.8382765572799656e+16 + ], + [ + NaN + ], + [ + -2.8703455778021874e-155 + ], + [ + 0.0 + ], + [ + -5.725336906712863e+268 + ], + [ + -5.725336906712863e+268 + ], + [ + -6.8382765572799656e+16 + ], + [ + -6.8382765572799656e+16 + ] + ], + "tensor_b": [ + [ + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + 0.0 + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ], + [ + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ], + [ + -Infinity + ] + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + [ + 0.0 + ] + ], + "tensor_b": [ + [ + [ + 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 + ], + [ + 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 + ], + [ + 0.0 + ], + [ + 0.0 + ] + ], + [ + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + -Infinity + ], + [ + 0.0 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + [ + -Infinity + ] + ], + "tensor_b": [ + [ + [ + 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 + ], + [ + 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 + ], + [ + 0.0 + ], + [ + 0.0 + ] + ], + [ + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP16", + "tensor_a": [ + [ + -Infinity + ] + ], + "tensor_b": [ + [ + [ + 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 + ], + [ + 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 + ], + [ + 0.0 + ], + [ + 0.0 + ] + ], + [ + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ], + [ + 0.0 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + -0.0 + ], + "tensor_b": [ + -2.0000100135803223, + 4.86998782550999e+16, + -6.134710524287386e+16, + -Infinity, + -0.0 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP32", + "tensor_a": [ + -0.0 + ], + "tensor_b": [ + -2.0000100135803223, + 4.86998782550999e+16, + -6.134710524287386e+16, + -Infinity, + -0.0 + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "invalid value encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + [ + [ + -6.678851490691534e-51 + ] + ] + ], + [ + [ + [ + 2.234689091837863e+45 + ] + ] + ], + [ + [ + [ + 5.166626287350241e+260 + ] + ] + ], + [ + [ + [ + -2.2228196160631336e+16 + ] + ] + ], + [ + [ + [ + 5.166626287350241e+260 + ] + ] + ], + [ + [ + [ + 0.0 + ] + ] + ], + [ + [ + [ + -1.5898166511059508e+16 + ] + ] + ], + [ + [ + [ + 1.175494351e-38 + ] + ] + ], + [ + [ + [ + Infinity + ] + ] + ], + [ + [ + [ + 0.0 + ] + ] + ] + ], + "tensor_b": [ + [ + 0.0 + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + [ + [ + -6.678851490691534e-51 + ] + ] + ], + [ + [ + [ + 2.234689091837863e+45 + ] + ] + ], + [ + [ + [ + 5.166626287350241e+260 + ] + ] + ], + [ + [ + [ + -2.2228196160631336e+16 + ] + ] + ], + [ + [ + [ + 5.166626287350241e+260 + ] + ] + ], + [ + [ + [ + 0.0 + ] + ] + ], + [ + [ + [ + -1.5898166511059508e+16 + ] + ] + ], + [ + [ + [ + 1.175494351e-38 + ] + ] + ], + [ + [ + [ + Infinity + ] + ] + ], + [ + [ + [ + 0.0 + ] + ] + ] + ], + "tensor_b": [ + [ + 5.166626287350241e+260 + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + -3.128688447954974e+16, + 1.5, + -3.128688447954974e+16, + -5.6394925605159896e+16, + -2.9082818668073108e+16, + -3.128688447954974e+16 + ], + "tensor_b": [ + [ + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + 5.960464477539063e-08, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -10000000.0 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + 1.2237054399270284e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + 1.664673175910764e+16 + ], + [ + 2.071402385040673e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + 4.218710097575574e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -2.7616271258545876e+16, + -5.537445846459022e+16, + 2.774443501421708e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + NaN, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -3.1634222896937908e-292, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + 1.8239965825468027e+274, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -4.6798320629421096e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + Infinity, + -5.537445846459022e+16, + -5.537445846459022e+16, + -1.3466906262788146e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + 2.220446049250313e-16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ] + ], + [ + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + 4091002101616927.0, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + 2.0962351632021156e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -3.084743758119207e-291, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -1.84941327012157e-246, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -4.8589100348942986e-259, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -6.096179044251506e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + 2613948384276712.0, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -2907251264750943.0, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -1.4700608568713611e+230, + -5.537445846459022e+16, + -3.1540371713548435e-125, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ], + [ + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + 1.0408117315840117e-106, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -1.3266305946583449e-17, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + 2.2932050320939507e+298 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ], + [ + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16, + -5.537445846459022e+16 + ] + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + 5.541169972896393e+16 + ], + "tensor_b": 1.7976931348623157e+308 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + [ + -7.802940770895372e+155 + ] + ] + ], + "tensor_b": [ + [ + [ + 2.037625145248553e+16 + ] + ], + [ + [ + -5.959583882487002e+16 + ] + ], + [ + [ + -Infinity + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 6.465689481319161e+220 + ] + ], + [ + [ + 6.172519310594286e+16 + ] + ], + [ + [ + -7.194487236465736e+16 + ] + ], + [ + [ + -6.377634776759738e+16 + ] + ], + [ + [ + -2.6149477558027483e+265 + ] + ], + [ + [ + -8.544422119909794e+263 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + [ + -7.802940770895372e+155 + ] + ] + ], + "tensor_b": [ + [ + [ + 0.0 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + 6.172519310594286e+16 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + -6.377634776759738e+16 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + -8.544422119909794e+263 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": [ + [ + [ + -7.802940770895372e+155 + ] + ] + ], + "tensor_b": [ + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 0.0 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + 6.172519310594286e+16 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + -6.377634776759738e+16 + ] + ], + [ + [ + 8315513190156478.0 + ] + ], + [ + [ + -8.544422119909794e+263 + ] + ] + ] + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -4.142287054452582e+268, + "tensor_b": -4.142287054452582e+268 + }, + { + "type": "Warning Math\u00e9matique", + "detail": "overflow encountered in multiply", + "dtype": "FP64", + "tensor_a": -4.142287054452582e+268, + "tensor_b": -4.142287054452582e+268 + } + ] +} \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/neg/README.md b/safety-related-profile/sonnx/ops/spec/tests/neg/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/pad/README.md b/safety-related-profile/sonnx/ops/spec/tests/pad/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/pow/README.md b/safety-related-profile/sonnx/ops/spec/tests/pow/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/pow/pow_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/pow/pow_doc.ipynb new file mode 100644 index 00000000..3edb3295 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/pow/pow_doc.ipynb @@ -0,0 +1,1027 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Matrix of A values:\n", + "[[ -inf -inf -inf -inf -inf\n", + " -inf -inf -inf -inf -inf\n", + " -inf]\n", + " [-10.00000000 -10.00000000 -10.00000000 -10.00000000 -10.00000000\n", + " -10.00000000 -10.00000000 -10.00000000 -10.00000000 -10.00000000\n", + " -10.00000000]\n", + " [ -0.50000000 -0.50000000 -0.50000000 -0.50000000 -0.50000000\n", + " -0.50000000 -0.50000000 -0.50000000 -0.50000000 -0.50000000\n", + " -0.50000000]\n", + " [ -0.33333334 -0.33333334 -0.33333334 -0.33333334 -0.33333334\n", + " -0.33333334 -0.33333334 -0.33333334 -0.33333334 -0.33333334\n", + " -0.33333334]\n", + " [ -0.00000000 -0.00000000 -0.00000000 -0.00000000 -0.00000000\n", + " -0.00000000 -0.00000000 -0.00000000 -0.00000000 -0.00000000\n", + " -0.00000000]\n", + " [ 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000\n", + " 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000\n", + " 0.00000000]\n", + " [ 0.33333334 0.33333334 0.33333334 0.33333334 0.33333334\n", + " 0.33333334 0.33333334 0.33333334 0.33333334 0.33333334\n", + " 0.33333334]\n", + " [ 0.50000000 0.50000000 0.50000000 0.50000000 0.50000000\n", + " 0.50000000 0.50000000 0.50000000 0.50000000 0.50000000\n", + " 0.50000000]\n", + " [ 10.00000000 10.00000000 10.00000000 10.00000000 10.00000000\n", + " 10.00000000 10.00000000 10.00000000 10.00000000 10.00000000\n", + " 10.00000000]\n", + " [ inf inf inf inf inf\n", + " inf inf inf inf inf\n", + " inf]\n", + " [ nan nan nan nan nan\n", + " nan nan nan nan nan\n", + " nan]]\n", + "\n", + "Matrix of B values:\n", + "[[ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]\n", + " [ -inf -10.00000000 -0.50000000 -0.33333334 -0.00000000\n", + " 0.00000000 0.33333334 0.50000000 10.00000000 inf\n", + " nan]]\n", + "A=[[ -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf, -inf], [-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000,-10.00000000], [ -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000, -0.50000000], [ -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334, -0.33333334], [ -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000, -0.00000000], [ 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000], [ 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334, 0.33333334], [ 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000, 0.50000000], [ 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000, 10.00000000], [ inf, inf, inf, inf, inf, inf, inf, inf, inf, inf, inf], [ nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]]\n", + "B=[[ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan], [ -inf,-10.00000000, -0.50000000, -0.33333334, -0.00000000, 0.00000000, 0.33333334, 0.50000000, 10.00000000, inf, nan]]\n", + "Result: A^B=[[0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,1.00000000e+00,1.00000000e+00, inf, inf, inf, inf, nan], [0.00000000e+00,1.00000001e-10, nan, nan,1.00000000e+00,1.00000000e+00, nan, nan,1.00000000e+10, inf, nan], [ inf,1.02400000e+03, nan, nan,1.00000000e+00,1.00000000e+00, nan, nan,9.76562500e-04,0.00000000e+00, nan], [ inf,5.90489805e+04, nan, nan,1.00000000e+00,1.00000000e+00, nan, nan,1.69350933e-05,0.00000000e+00, nan], [ inf, inf, inf, inf,1.00000000e+00,1.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00, nan], [ inf, inf, inf, inf,1.00000000e+00,1.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00, nan], [ inf,5.90489805e+04,1.73205078e+00,1.44224954e+00,1.00000000e+00,1.00000000e+00,6.93361282e-01,5.77350259e-01,1.69350933e-05,0.00000000e+00, nan], [ inf,1.02400000e+03,1.41421354e+00,1.25992107e+00,1.00000000e+00,1.00000000e+00,7.93700516e-01,7.07106769e-01,9.76562500e-04,0.00000000e+00, nan], [0.00000000e+00,1.00000001e-10,3.16227764e-01,4.64158863e-01,1.00000000e+00,1.00000000e+00,2.15443468e+00,3.16227770e+00,1.00000000e+10, inf, nan], [0.00000000e+00,0.00000000e+00,0.00000000e+00,0.00000000e+00,1.00000000e+00,1.00000000e+00, inf, inf, inf, inf, nan], [ nan, nan, nan, nan,1.00000000e+00,1.00000000e+00, nan, nan, nan, nan, nan]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name_A = \"A\"\n", + "input_name_B = \"B\"\n", + "output_name = \"Output\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor for A and B\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensors for A and B\n", + "input_tensor_A = onnx.helper.make_tensor_value_info(input_name_A, onnx.TensorProto.FLOAT, [None, None])\n", + "input_tensor_B = onnx.helper.make_tensor_value_info(input_name_B, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor for A^B\n", + "output_tensor = onnx.helper.make_tensor_value_info(output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define Power node: Y = A^B\n", + "power_node = onnx.helper.make_node(\"Pow\", [input_name_A, input_name_B], [output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[power_node],\n", + " name=\"Power\",\n", + " inputs=[input_tensor_A, input_tensor_B],\n", + " outputs=[output_tensor]\n", + ")\n", + "\n", + "# Create the ONNX model (Pow is available in opset 13)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"power.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"power.onnx\")\n", + "\n", + "def do_power(A, B):\n", + " # Run inference\n", + " output = session.run(None, {input_name_A: A, input_name_B: B})\n", + "\n", + " A_f = (np.array2string(A, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " B_f = (np.array2string(B, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " O_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"A={A_f}\")\n", + " print(f\"B={B_f}\")\n", + " print(f\"Result: A^B={O_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Values for A and B (as per your test set)\n", + "#--------------------------------------------------------------------\n", + "\n", + "values = [float(\"-inf\"), -10.0, -1/2, -1/3, -0.0, 0.0, 1/3,1/2, 10.0, float(\"inf\"), float(\"nan\")]\n", + "\n", + "# Create a matrix where each row is a combination of A and B values\n", + "# The matrix will have A as the first dimension and B as the second dimension\n", + "\n", + "A_vals = np.array(values).reshape(-1, 1) # Reshape for broadcasting\n", + "B_vals = np.array(values).reshape(1, -1) # Reshape for broadcasting\n", + "\n", + "# Create a full matrix with all combinations of A and B\n", + "X_A = np.tile(A_vals, (1, len(values))) # Repeat A values in rows\n", + "X_B = np.tile(B_vals, (len(values), 1)) # Repeat B values in columns\n", + "\n", + "# Convert matrices to float32 to avoid type mismatch\n", + "X_A = X_A.astype(np.float32)\n", + "X_B = X_B.astype(np.float32)\n", + "\n", + "# Now, X_A and X_B are matrices with A and B values respectively\n", + "print(\"Matrix of A values:\")\n", + "print(X_A)\n", + "\n", + "print(\"\\nMatrix of B values:\")\n", + "print(X_B)\n", + "\n", + "# Run the power operation (A^B) on the matrix\n", + "do_power(X_A, X_B)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A=[[ 9.00000000, 4.00000000,16.00000000, 8.00000000, 2.00000000]]\n", + "B=[[2.00000000,2.50000000,0.50000000,0.33333334,1.50000000]]\n", + "Result: A^B=[[81.00000000,32.00000000, 4.00000000, 2.00000000, 2.82842708]]\n", + "A=[[1.00000000]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[inf]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[1.00000000]]\n", + "B=[[nan]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[0.00000000]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[0.00000000]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[0.00000000]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[0.00000000]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[inf]]\n", + "A=[[0.00000000]]\n", + "B=[[inf]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[0.00000000]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[inf]]\n", + "A=[[0.00000000]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "A=[[-0.00000000]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[-0.00000000]]\n", + "A=[[-0.00000000]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-0.00000000]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-0.00000000]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[-inf]]\n", + "A=[[-0.00000000]]\n", + "B=[[inf]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[-0.00000000]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[inf]]\n", + "A=[[-0.00000000]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "A=[[-1.00000000]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[-1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[-1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[inf]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-1.00000000]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "A=[[inf]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[inf]]\n", + "A=[[inf]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[inf]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[inf]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[inf]]\n", + "B=[[inf]]\n", + "Result: A^B=[[inf]]\n", + "A=[[inf]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[inf]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "A=[[-inf]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[-inf]]\n", + "A=[[-inf]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-inf]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[-inf]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[-0.00000000]]\n", + "A=[[-inf]]\n", + "B=[[inf]]\n", + "Result: A^B=[[inf]]\n", + "A=[[-inf]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[0.00000000]]\n", + "A=[[-inf]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "A=[[nan]]\n", + "B=[[1.00000000]]\n", + "Result: A^B=[[nan]]\n", + "A=[[nan]]\n", + "B=[[0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[nan]]\n", + "B=[[-0.00000000]]\n", + "Result: A^B=[[1.00000000]]\n", + "A=[[nan]]\n", + "B=[[-1.00000000]]\n", + "Result: A^B=[[nan]]\n", + "A=[[nan]]\n", + "B=[[inf]]\n", + "Result: A^B=[[nan]]\n", + "A=[[nan]]\n", + "B=[[-inf]]\n", + "Result: A^B=[[nan]]\n", + "A=[[nan]]\n", + "B=[[nan]]\n", + "Result: A^B=[[nan]]\n", + "\n", + "## Example 1\n", + "A=[[ 9.00000000, 4.00000000,16.00000000, 8.00000000, 2.00000000]]\n", + "B=[[2.00000000,2.50000000,0.50000000,0.33333334,1.50000000]]\n", + "Result: A^B=[[81.00000000,32.00000000, 4.00000000, 2.00000000, 2.82842708]]\n", + "\n", + "## Example 2\n", + "A=[[ 0.00000000, 0.00000000, 5.00000000, -5.00000000,-25.00000000, -8.00000000]]\n", + "B=[[0.00000000,2.00000000,0.00000000,0.00000000,0.60000002,0.33333334]]\n", + "Result: A^B=[[1.00000000,0.00000000,1.00000000,1.00000000, nan, nan]]\n", + "A=[[-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000,-8.00000000]]\n", + "B=[[ 0.00000000, 2.00000000, 3.00000000,-2.00000000,-3.00000000, 2.00000000, 3.00000000, 2.50000000,-0.33333334,-0.20000000,-0.16666667, 0.33333334, 0.20000000, 0.16666667,-0.33333331,-0.33333334,-0.33333334,-0.33333334,-0.33333334,-0.33333331,-0.33333334, 0.33333334, 0.33333334, 0.33333334]]\n", + "Result: A^B=[[ 1.00000000e+00, 6.40000000e+01,-5.12000000e+02, 1.56250000e-02,-1.95312500e-03, 6.40000000e+01,-5.12000000e+02, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan]]\n", + "\n", + "## Example 1\n", + "A=[[ 9.00000000, 4.00000000,16.00000000, 8.00000000, 2.00000000]]\n", + "B=[[2.00000000,2.50000000,0.50000000,0.33333334,1.50000000]]\n", + "Result: A^B=[[81.00000000,32.00000000, 4.00000000, 2.00000000, 2.82842708]]\n", + "\n", + "## Example 2\n", + "A=[[ 0.00000000, 0.00000000, 5.00000000, -5.00000000,-25.00000000, -8.00000000]]\n", + "B=[[0.00000000,2.00000000,0.00000000,0.00000000,0.60000002,0.33333334]]\n", + "Result: A^B=[[1.00000000,0.00000000,1.00000000,1.00000000, nan, nan]]\n", + "\n", + "## Example 3\n", + "A=[[-2.00000000,-2.00000000,-1.00000000,-1.00000000, 0.00000000,-0.00000000, 2.00000000, 0.50000000, 2.00000000]]\n", + "B=[[ 0.50000000, 3.00000000, inf, -inf,-3.00000000,-3.00000000, -inf, inf, nan]]\n", + "Result: A^B=[[ nan,-8.00000000, 1.00000000, 1.00000000, inf, -inf, 0.00000000, 0.00000000, nan]]\n", + "\n", + "## Example 4\n", + "A=[[ nan, 1.00000000,-1.00000000, -inf, -inf, inf, 0.50000000, 2.00000000,-0.00000000]]\n", + "B=[[ 2.00000000, -inf, inf, 3.00000000,-2.00000000,-1.00000000, -inf, inf, 3.00000000]]\n", + "Result: A^B=[[ nan, 1.00000000, 1.00000000, -inf, 0.00000000, 0.00000000, inf, inf,-0.00000000]]\n", + "A=[[-8.00000000e+00,-8.00000000e+00,-8.00000000e+00,-8.00000000e+00,-8.00000000e+00, 9.99999984e+16]]\n", + "B=[[2.00000000e+00,2.00000000e+00,2.00000000e+00,2.00000024e+00,2.00000024e+00,9.99999984e+16]]\n", + "Result: A^B=[[64.00000000,64.00000000,64.00000000, nan, nan, inf]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"A\"\n", + "pow_output_name = \"PowOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor for A and B\n", + "input_tensor_A = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "input_tensor_B = onnx.helper.make_tensor_value_info(\"B\", onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after POW operation)\n", + "pow_output = onnx.helper.make_tensor_value_info(pow_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define POW node: Y = A^B\n", + "pow_node = onnx.helper.make_node(\"Pow\", [input_name, \"B\"], [pow_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[pow_node],\n", + " name=\"Pow\",\n", + " inputs=[input_tensor_A, input_tensor_B],\n", + " outputs=[pow_output]\n", + ")\n", + "\n", + "# Create the ONNX model (POW is available in opset 13)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"pow.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"pow.onnx\")\n", + "\n", + "def do_pow(x_A, x_B):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x_A, \"B\": x_B})\n", + "\n", + " x_A_f = np.array2string(x_A, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " x_B_f = np.array2string(x_B, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " # Display results\n", + " print(f\"A={x_A_f}\")\n", + " print(f\"B={x_B_f}\")\n", + " print(f\"Result: A^B={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "A = np.array([[9.0, 4.0, 16.0, 8.0, 2.0]], dtype=np.float32)\n", + "B = np.array([[2.0, 2.5, 0.5, 1/3, 1.5]], dtype=np.float32)\n", + "\n", + "do_pow(A, B)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, -0, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_A_val in v:\n", + " for x_B_val in v:\n", + " A_np = np.array([[x_A_val]], dtype=np.float32)\n", + " B_np = np.array([[x_B_val]], dtype=np.float32)\n", + " do_pow(A_np, B_np)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# A = [9, 4, 16, 8, 2]\n", + "# B = [2, 2.5, 0.5, 1/3, 3/2]\n", + "x_A_example_1 = np.array([[9.0, 4.0, 16.0, 8.0, 2.0]], dtype=np.float32)\n", + "x_B_example_1 = np.array([[2.0, 2.5, 0.5, 1/3, 1.5]], dtype=np.float32)\n", + "do_pow(x_A_example_1, x_B_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# A = [0, 0, 5, -5, -25, -8]\n", + "# B = [0, 2, 0, 0, 3/5, 1/3]\n", + "x_A_example_2 = np.array([[0.0, 0.0, 5.0, -5.0, -25.0, -8.0]], dtype=np.float32)\n", + "x_B_example_2 = np.array([[0.0, 2.0, 0.0, 0.0, 3/5, 1/3]], dtype=np.float32)\n", + "do_pow(x_A_example_2, x_B_example_2)\n", + "\n", + "\n", + "\n", + "# Example 3\n", + "x_A_example_2 = np.array([[-8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0, -8.0]], dtype=np.float32)\n", + "x_B_example_2 = np.array([[0.0, 2.0, 3.0, -2.0, -3.0, 2.0, 3.0, 2.5, -1/3, -1/5, -1/6, 1/3, 1/5, 1/6, -0.3333333, -0.33333333, -0.333333333, -0.3333333333, -0.33333333333, -0.3333333, -0.33333333, 0.333333333, 0.3333333333, 0.33333333333]], dtype=np.float32)\n", + "do_pow(x_A_example_2, x_B_example_2)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "print(\"\\n## Example 1\")\n", + "# Example 1\n", + "# A = [9, 4, 16, 8, 2]\n", + "# B = [2, 2.5, 0.5, 1/3, 3/2]\n", + "x_A_example_1 = np.array([[9.0, 4.0, 16.0, 8.0, 2.0]], dtype=np.float32)\n", + "x_B_example_1 = np.array([[2.0, 2.5, 0.5, 1/3, 1.5]], dtype=np.float32)\n", + "do_pow(x_A_example_1, x_B_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# A = [0, 0, 5, -5, -25, -8]\n", + "# B = [0, 2, 0, 0, 3/5, 1/3]\n", + "x_A_example_2 = np.array([[0.0, 0.0, 5.0, -5.0, -25.0, -8.0]], dtype=np.float32)\n", + "x_B_example_2 = np.array([[0.0, 2.0, 0.0, 0.0, 3/5, 1/3]], dtype=np.float32)\n", + "do_pow(x_A_example_2, x_B_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# A = [-2, -2, -1, -1, 0, -0, 2, 0.5, 2]\n", + "# B = [0.5, 3, +inf, -inf, -3, -3, -inf, +inf, NaN]\n", + "x_A_example_3 = np.array([[-2.0, -2.0, -1.0, -1.0, 0.0, -0.0, 2.0, 0.5, 2.0]],dtype=np.float32)\n", + "x_B_example_3 = np.array([[0.5, 3.0, np.inf, -np.inf, -3.0, -3.0, -np.inf, np.inf, np.nan]],dtype=np.float32)\n", + "do_pow(x_A_example_3, x_B_example_3)\n", + "\n", + "print(\"\\n## Example 4\")\n", + "\n", + "# Example 4\n", + "# A = [NaN, 1, -1, -inf, -inf, +inf, 0.5, 2, -0]\n", + "# B = [2, -inf, +inf, 3, -2, -1, -inf, +inf, 3]\n", + "x_A_example_4 = np.array([[np.nan, 1.0, -1.0, -np.inf, -np.inf, np.inf, 0.5, 2.0, -0.0]],dtype=np.float32)\n", + "x_B_example_4 = np.array([[2.0, -np.inf, np.inf, 3.0, -2.0, -1.0, -np.inf, np.inf, 3.0]],dtype=np.float32)\n", + "do_pow(x_A_example_4, x_B_example_4)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "# Example interessant ou l'on passe de 64 a Nan\n", + "x_A_example_2 = np.array([[-8.0, -8.0, -8.0, -8.0, -8.0, 99999999999999999.9]], dtype=np.float32)\n", + "x_B_example_2 = np.array([[2.00000000, 2.00000010, 2.00000011, 2.00000012, np.nextafter(np.float32(2.0),np.float32(2.1)), 99999999999999999.9 ]], dtype=np.float32)\n", + "do_pow(x_A_example_2, x_B_example_2)\n", + "\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Test of the POW SPEC" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(x, NaN) | returns NaN |\n", + "A=[[ -2.00000000, -4.00000000, -1.00000000, 0.10000000, 1.50000000, 2.00000000, 10.50000000,-10.50000000, 0.00000000, -0.00000000, -0.50000000, 0.33333334, inf, -inf, nan]]\n", + "B=[[nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan]]\n", + "Result: A^B=[[nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan]]\n", + "---\n", + "| pow(NaN, y) with y != ±0 | returns NaN |\n", + "A=[[nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan]]\n", + "B=[[ 0.10000000,-0.10000000, 2.00000000,-2.00000000, 1.50000000,-1.50000000,-0.50000000, 0.33333334, inf, -inf, nan]]\n", + "Result: A^B=[[nan,nan,nan,nan,nan,nan,nan,nan,nan,nan,nan]]\n", + "---\n", + "| pow(negative finite, non-integer) | returns NaN |\n", + "A=[[ -0.10000000, -0.50000000, -1.10000002, -2.00000000,-10.50000000, -0.33333334, -0.50000000, -7.00000000]]\n", + "B=[[ 0.10000000, 0.33333334, 0.50000000, 1.10000002, 2.50000000,-0.33333334,-0.50000000, 0.25000000]]\n", + "Result: A^B=[[nan,nan,nan,nan,nan,nan,nan,nan]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(x, NaN) | returns NaN |\")\n", + "# Condition: B[i] = NaN → résultat attendu: NaN\n", + "x_A = np.array([[-2, -4, -1.0, 0.1, 1.5, 2.0, 10.5, -10.5, +0.0, -0.0, -1/2, 1/3, float(\"inf\"), float(\"-inf\"), float(\"nan\")]], dtype=np.float32)\n", + "x_B = np.array([[float(\"nan\")] * x_A.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: NaN\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(NaN, y) with y != ±0 | returns NaN |\")\n", + "# Condition: A[i] = NaN AND B[i] != ±0 → résultat attendu: NaN\n", + "x_B = np.array([[0.1, -0.1, 2.0, -2.0, 1.5, -1.5, -1/2, 1/3,float(\"inf\"), float(\"-inf\"), float(\"nan\")]], dtype=np.float32)\n", + "x_A = np.array([[float(\"nan\")] * x_B.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: NaN\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(negative finite, non-integer) | returns NaN |\")\n", + "# Condition: A[i] < 0 (fini) AND B[i] non entier → résultat attendu: NaN\n", + "x_A = np.array([[-0.1, -0.5, -1.1, -2.0, -10.5, -1/3, -1/2, -7.0]], dtype=np.float32)\n", + "x_B = np.array([[0.1, 1/3, 0.5, 1.1, 2.5, -1/3, -0.5, 0.25]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: NaN\n", + "print(\"---\")" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(|x|>1, +inf) | returns +Inf |\n", + "A=[[ 1.10000002, 2.00000000, 10.50000000, -1.10000002, -2.00000000,-10.50000000]]\n", + "B=[[inf,inf,inf,inf,inf,inf]]\n", + "Result: A^B=[[inf,inf,inf,inf,inf,inf]]\n", + "---\n", + "| pow(+0, negative odd integer) | returns +Inf |\n", + "A=[[0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "B=[[-1.00000000,-3.00000000,-5.00000000,-7.00000000]]\n", + "Result: A^B=[[inf,inf,inf,inf]]\n", + "---\n", + "| pow(±0, negative non-odd) | returns +Inf |\n", + "A=[[ 0.00000000,-0.00000000, 0.00000000,-0.00000000, 0.00000000,-0.00000000, 0.00000000,-0.00000000]]\n", + "B=[[-2.00000000,-0.50000000,-0.33300000,-0.33300000,-4.00000000,-4.00000000,-1.00000000,-2.00000000]]\n", + "Result: A^B=[[inf,inf,inf,inf,inf,inf,inf,inf]]\n", + "---\n", + "| pow(+Inf, positive) | returns +Inf |\n", + "A=[[inf,inf,inf,inf,inf,inf,inf]]\n", + "B=[[ 0.10000000, 1.00000000, 2.00000000, 10.50000000,100.00000000, 0.33333334, inf]]\n", + "Result: A^B=[[inf,inf,inf,inf,inf,inf,inf]]\n", + "---\n", + "| pow(-Inf, positive non-odd) | returns +Inf |\n", + "A=[[-inf,-inf,-inf,-inf,-inf,-inf,-inf]]\n", + "B=[[ 2.00000000, 4.00000000, 0.50000000,10.00000000, 0.33333334, inf, 6.00000000]]\n", + "Result: A^B=[[inf,inf,inf,inf,inf,inf,inf]]\n", + "---\n", + "| pow(|x|<1, -Inf) | returns +Inf |\n", + "A=[[ 0.10000000, 0.50000000, 0.99999899,-0.10000000,-0.50000000,-0.99999899]]\n", + "B=[[-inf,-inf,-inf,-inf,-inf,-inf]]\n", + "Result: A^B=[[inf,inf,inf,inf,inf,inf]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(|x|>1, +inf) | returns +Inf |\")\n", + "# Condition: B[i] = +∞ AND |A[i]| > 1 → résultat attendu: +Inf\n", + "x_A = np.array([[1.1, 2.0, 10.5, -1.1, -2.0, -10.5]], dtype=np.float32)\n", + "x_B = np.array([[float(\"inf\")] * x_A.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(+0, negative odd integer) | returns +Inf |\")\n", + "# Condition: A[i] = +0 AND B[i] negative odd integer → résultat attendu: +Inf\n", + "x_A = np.array([[+0.0, +0.0, 0.0, 0.0]], dtype=np.float32)\n", + "x_B = np.array([[-1, -3, -5, -7]], dtype=np.float32) # negative odd integers\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(±0, negative non-odd) | returns +Inf |\")\n", + "# Condition: A[i] = ±0 AND B[i] < 0 AND B[i] non odd integer → résultat attendu: +Inf\n", + "x_A = np.array([[+0.0, -0.0,+0.0, -0.0,+0.0, -0.0,+0.0, -0.0]], dtype=np.float32)\n", + "x_B = np.array([[-2.0, -0.5,-0.333, -0.333,-4.0, -4.0,-1.0, -2.0]], dtype=np.float32) # negative non-odd numbers\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(+Inf, positive) | returns +Inf |\")\n", + "# Condition: A[i] = +Inf AND B[i] > 0 → résultat attendu: +Inf\n", + "x_A = np.array([[float(\"inf\")] * 7], dtype=np.float32)\n", + "x_B = np.array([[0.1, 1.0, 2.0, 10.5, 100.0, 1/3, float(\"inf\")]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(-Inf, positive non-odd) | returns +Inf |\")\n", + "# Condition: A[i] = -Inf AND B[i] > 0 AND B[i] non odd integer → résultat attendu: +Inf\n", + "x_A = np.array([[float(\"-inf\")] * 7], dtype=np.float32)\n", + "x_B = np.array([[2.0, 4.0, 0.5, 10.0, 1/3, float(\"inf\"), 6.0]], dtype=np.float32) # positive non-odd numbers\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(|x|<1, -Inf) | returns +Inf |\")\n", + "# Condition: B[i] = -Inf AND 0 < |A[i]| < 1 → résultat attendu: +Inf\n", + "x_A = np.array([[0.1, 0.5, 0.999999, -0.1, -0.5, -0.999999]], dtype=np.float32)\n", + "x_B = np.array([[float(\"-inf\")] * x_A.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +Inf\n", + "print(\"---\")\n", + "\n", + "\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(-Inf, positive odd integer) | returns -Inf |\n", + "A=[[-inf,-inf,-inf]]\n", + "B=[[ 1.00000000, 3.00000000,11.00000000]]\n", + "Result: A^B=[[-inf,-inf,-inf]]\n", + "---\n", + "| pow(-0, negative odd integer) | returns -Inf |\n", + "A=[[-0.00000000,-0.00000000,-0.00000000]]\n", + "B=[[ -1.00000000, -3.00000000,-11.00000000]]\n", + "Result: A^B=[[-inf,-inf,-inf]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(-Inf, positive odd integer) | returns -Inf |\")\n", + "# Condition: A[i] = -Inf AND B[i] positive odd integer → résultat attendu: -Inf\n", + "x_A = np.array([[float(\"-inf\"), float(\"-inf\"), float(\"-inf\")]], dtype=np.float32)\n", + "x_B = np.array([[1, 3, 11]], dtype=np.float32) # positive odd integers\n", + "do_pow(x_A, x_B) # Expected: -Inf\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(-0, negative odd integer) | returns -Inf |\")\n", + "# Condition: A[i] = -0 AND B[i] negative odd integer → résultat attendu: -Inf\n", + "x_A = np.array([[-0.0, -0.0, -0.0]], dtype=np.float32)\n", + "x_B = np.array([[-1, -3, -11]], dtype=np.float32) # negative odd integers\n", + "do_pow(x_A, x_B) # Expected: -Inf\n", + "print(\"---\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(|x|<1, +Inf) | returns +0 |\n", + "A=[[ 0.10000000, 0.50000000, 0.99999899,-0.10000000,-0.50000000,-0.99999899, 0.00000000, 0.00000000]]\n", + "B=[[inf,inf,inf,inf,inf,inf,inf,inf]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "---\n", + "| pow(-Inf, negative non-odd) | returns +0 |\n", + "A=[[-inf,-inf,-inf,-inf]]\n", + "B=[[ -2.00000000, -0.50000000, -4.00000000,-10.50000000]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "---\n", + "| pow(+0, positive odd integer) | returns +0 |\n", + "A=[[0.00000000,0.00000000,0.00000000]]\n", + "B=[[ 1.00000000, 3.00000000,11.00000000]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000]]\n", + "---\n", + "| pow(±0, positive non-odd) | returns +0 |\n", + "A=[[ 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000, 0.00000000,-0.00000000,-0.00000000,-0.00000000,-0.00000000,-0.00000000,-0.00000000]]\n", + "B=[[2.00000000,4.00000000,0.50000000,2.00000000,2.50000000,0.50000000,2.00000000,4.00000000,0.50000000,2.00000000,2.50000000,0.50000000]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "---\n", + "| pow(+Inf, negative) | returns +0 |\n", + "A=[[inf,inf,inf,inf,inf]]\n", + "B=[[ -0.10000000, -2.00000000, -10.50000000,-100.00000000, -inf]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "---\n", + "| pow(|x|>1, -Inf) | returns +0 |\n", + "A=[[ 1.10000002, 2.00000000, 10.50000000, -1.10000002, -2.00000000,-10.50000000, inf]]\n", + "B=[[-inf,-inf,-inf,-inf,-inf,-inf,-inf]]\n", + "Result: A^B=[[0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000,0.00000000]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(|x|<1, +Inf) | returns +0 |\")\n", + "# Condition: B[i] = +Inf AND 0 < |A[i]| < 1 → résultat attendu: +0\n", + "x_A = np.array([[0.1, 0.5, 0.999999, -0.1, -0.5, -0.999999, +0, -0]], dtype=np.float32)\n", + "x_B = np.array([[float(\"inf\")] * x_A.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(-Inf, negative non-odd) | returns +0 |\")\n", + "# Condition: A[i] = -Inf AND B[i] < 0 AND B[i] non odd integer → résultat attendu: +0\n", + "x_A = np.array([[float(\"-inf\")] * 4], dtype=np.float32)\n", + "x_B = np.array([[-2.0, -0.5, -4.0, -10.5]], dtype=np.float32) # negative non-odd numbers\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")\n", + "\n", + "\n", + "print(\"| pow(+0, positive odd integer) | returns +0 |\")\n", + "# Condition: A[i] = 0 AND B[i] positive odd integer → résultat attendu: +0\n", + "x_A = np.array([[0.0, 0.0, 0.0]], dtype=np.float32)\n", + "x_B = np.array([[1.0, 3.0, 11.0]], dtype=np.float32) # positive odd integers\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")\n", + "\n", + "\n", + "print(\"| pow(±0, positive non-odd) | returns +0 |\")\n", + "# Condition: A[i] = ±0 AND B[i] > 0 AND B[i] non odd integer → résultat attendu: +0\n", + "x_A = np.array([[+0.0, +0.0, +0.0, 0.0, 0.0, 0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0]], dtype=np.float32)\n", + "x_B = np.array([[2.0, 4.0, 0.5, 2.0, 2.5, 1/2, 2.0, 4.0, 0.5, 2.0, 2.5, 1/2]], dtype=np.float32) # positive non-odd numbers\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")\n", + "\n", + "\n", + "print(\"| pow(+Inf, negative) | returns +0 |\")\n", + "# Condition: A[i] = +Inf AND B[i] < 0 → résultat attendu: +0\n", + "x_A = np.array([[float(\"inf\")] * 5], dtype=np.float32)\n", + "x_B = np.array([[-0.1, -2.0, -10.5, -100.0, float(\"-inf\")]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(|x|>1, -Inf) | returns +0 |\")\n", + "# Condition: B[i] = -Inf AND |A[i]| > 1 → résultat attendu: +0\n", + "x_A = np.array([[1.1, 2.0, 10.5, -1.1, -2.0, -10.5, float(\"inf\")]], dtype=np.float32)\n", + "x_B = np.array([[float(\"-inf\")] * x_A.shape[1]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: +0\n", + "print(\"---\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(-0, positive odd integer) | returns -0 |\n", + "A=[[-0.00000000,-0.00000000,-0.00000000]]\n", + "B=[[ 1.00000000, 3.00000000,11.00000000]]\n", + "Result: A^B=[[-0.00000000,-0.00000000,-0.00000000]]\n", + "---\n", + "| pow(-Inf, negative odd integer) | returns -0 |\n", + "A=[[-inf,-inf,-inf,-inf]]\n", + "B=[[ -1.00000000, -3.00000000,-11.00000000,-11.50000000]]\n", + "Result: A^B=[[-0.00000000,-0.00000000,-0.00000000, 0.00000000]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(-0, positive odd integer) | returns -0 |\")\n", + "# Condition: A[i] = -0 AND B[i] positive odd integer → résultat attendu: -0\n", + "x_A = np.array([[-0.0, -0.0, -0.0]], dtype=np.float32)\n", + "x_B = np.array([[1, 3, 11]], dtype=np.float32) # positive odd integers\n", + "do_pow(x_A, x_B) # Expected: -0\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(-Inf, negative odd integer) | returns -0 |\")\n", + "# Condition: A[i] = -Inf AND B[i] negative odd integer → résultat attendu: -0\n", + "x_A = np.array([[float(\"-inf\"), float(\"-inf\"), float(\"-inf\")]], dtype=np.float32)\n", + "x_B = np.array([[-1, -3, -11]], dtype=np.float32) # negative odd integers\n", + "do_pow(x_A, x_B) # Expected: -0\n", + "print(\"---\")" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(-1, ±Inf) | returns 1 |\n", + "A=[[-1.00000000,-1.00000000]]\n", + "B=[[ inf,-inf]]\n", + "Result: A^B=[[1.00000000,1.00000000]]\n", + "---\n", + "| pow(+1, any) | returns 1 |\n", + "A=[[1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000]]\n", + "B=[[ -inf,-2.00000000,-0.33333334, 0.00000000,-0.00000000, 0.33333334, 2.00000000, inf, nan]]\n", + "Result: A^B=[[1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000]]\n", + "---\n", + "| pow(any, ±0) | returns 1 |\n", + "A=[[-10.00000000, -1.00000000, -0.50000000, 0.00000000, 0.50000000, 1.00000000, 10.00000000, inf, -inf, nan]]\n", + "B=[[ 0.00000000,-0.00000000, 0.00000000,-0.00000000, 0.00000000,-0.00000000, 0.00000000,-0.00000000, 0.00000000,-0.00000000]]\n", + "Result: A^B=[[1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000,1.00000000]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(-1, ±Inf) | returns 1 |\")\n", + "# Condition: A[i] = -1 AND B[i] = ±Inf → résultat attendu: NaN\n", + "x_A = np.array([[-1.0, -1.0]], dtype=np.float32)\n", + "x_B = np.array([[float(\"inf\"), float(\"-inf\")]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: 1\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(+1, any) | returns 1 |\")\n", + "# Condition: A[i] = +1 AND B[i] any value → résultat attendu: 1\n", + "x_A = np.array([[1, 1, 1, 1, 1, 1, 1, 1, 1]], dtype=np.float32)\n", + "x_B = np.array([[float(\"-inf\"), -2, -1/3, 0.0, -0.0, 1/3, 2, float(\"inf\"), float(\"nan\")]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: 1\n", + "print(\"---\")\n", + "\n", + "print(\"| pow(any, ±0) | returns 1 |\")\n", + "# Condition: B[i] = ±0 AND A[i] any value → résultat attendu: 1\n", + "x_A = np.array([[-10, -1, -0.5, 0.0, 0.5, 1, 10, float(\"inf\"), float(\"-inf\"), float(\"nan\")]], dtype=np.float32)\n", + "x_B = np.array([[0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0, 0.0, -0.0]], dtype=np.float32)\n", + "do_pow(x_A, x_B) # Expected: 1\n", + "print(\"---\")" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "| pow(A[i], B[i]) | normal cases |\n", + "A=[[ 0.10000000, 0.50000000, 2.00000000, 3.00000000,10.50000000,-1.00000000,-2.00000000,-3.00000000]]\n", + "B=[[2.00000000,3.00000000,0.50000000,1.10000002,2.00000000,2.00000000,3.00000000,4.00000000]]\n", + "Result: A^B=[[ 1.00000007e-02, 1.25000000e-01, 1.41421354e+00, 3.34836960e+00, 1.10250000e+02, 1.00000000e+00,-8.00000000e+00, 8.10000000e+01]]\n", + "---\n" + ] + } + ], + "source": [ + "import numpy as np\n", + "\n", + "print(\"| pow(A[i], B[i]) | normal cases |\")\n", + "# Condition: tous les autres cas → résultat attendu: calcul classique A[i]^B[i]\n", + "x_A = np.array([\n", + " [0.1, 0.5, 2.0, 3.0, 10.5, -1.0, -2.0, -3.0] # bases variées\n", + "], dtype=np.float32)\n", + "\n", + "x_B = np.array([\n", + " [2.0, 3.0, 0.5, 1.1, 2.0, 2.0, 3.0, 4.0] # exposants variés, tous valides\n", + "], dtype=np.float32)\n", + "\n", + "do_pow(x_A, x_B) # Expected: A[i]^B[i] classique\n", + "print(\"---\")" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/README.md b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/README.md new file mode 100644 index 00000000..9f599cca --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/README.md @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_range.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_range.py you need to do +```bash +pytest test_range.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_range.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_range.py + +# Explanations of Range corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Range tests +For the range operator we just **checked both the corner and edge cases** for the **start**, **limit**, **delta** and **input_type**. + +We consider **start**, **limit**, **delta** and **input_type** as **lines** since they are independent and we checked the corner and edge cases for both of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/check_edges.py new file mode 100644 index 00000000..d04d6b6b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/check_edges.py @@ -0,0 +1,116 @@ +""" +This file checks edge cases in the generated data for the Range operation in ONNX. +""" +import json +import numpy as np + +""" +Range supported types, organized by ONNXRuntime_Provider +""" +range_types = { + "CPUExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32, + "FP64": np.float64, + }, + "CUDAExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32, + "FP64": np.float64, + }, + "DmlExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32 + } +} + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + check_s_tensor_int = check_individual_variables( + "s_tensor (int)", data["s_tensor_int"], data["min_input_int"], data["max_input_int"] + ) + + check_s_tensor_float = check_individual_variables( + "s_tensor (float)", data["s_tensor_float"], + data["min_input_float"], data["max_input_float"] + ) + + check_l_tensor_int = check_individual_variables( + "l_tensor (int)", data["l_tensor_int"], + data["min_input_int"], data["max_input_int"] + ) + + check_l_tensor_float = check_individual_variables( + "l_tensor (float)", data["l_tensor_float"], + data["min_input_float"], data["max_input_float"] + ) + + check_d_tensor_int = check_individual_variables( + "d_tensor (int)", data["d_tensor_int"], + data["min_input_int"], data["max_input_int"] + ) + + check_d_tensor_float = check_individual_variables( + "d_tensor (float)", data["d_tensor_float"], + data["min_input_float"], data["max_input_float"] + ) + + check_input_type = check_type( + "input_type", data["input_type"], data["ONNXRuntime_Provider"] + ) + + return all([check_s_tensor_int, check_s_tensor_float, check_l_tensor_int, + check_l_tensor_float, check_d_tensor_int, + check_d_tensor_float, check_input_type]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(range_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + + +print(check_edges()) diff --git a/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/test_range.py b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/test_range.py new file mode 100644 index 00000000..0b2cba46 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/range/hypothesis/test_range.py @@ -0,0 +1,266 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Range operation in ONNX. +""" +import os + +import math +import json +import numpy as np + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + +from hypothesis import given, settings, assume +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes for Range operation +""" +inputs_attributes = { + "min_input_int": -1e6, # Adjust as needed + "max_input_int": 1e6, # Adjust as needed + "min_input_float": -1e7, # Adjust as needed + "max_input_float": 1e7, # Adjust as needed + "max_output_elements": 1e7, # Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + +""" +Store generated data +""" +generated_data = { + "s_tensor": [], + "l_tensor": [], + "d_tensor": [] +} + +""" +Range supported types, organized by ONNXRuntime_Provider +""" +range_types = { + "CPUExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32, + "FP64": np.float64, + }, + "CUDAExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32, + "FP64": np.float64, + }, + "DmlExecutionProvider": { + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "FP32": np.float32 + } +} + +dtype_to_key = {v: k for k, v in range_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Store generated data +""" +generated_data = { + "input_type": [], + "s_tensor": [], + "l_tensor": [], + "d_tensor": [] +} + +def calculate_y_shape(s, l, d): + """ + Function to calculate the expected output shape of Range operation + """ + return max(math.ceil((l - s) / d), 0) + +""" +Function to generate valid range arguments +""" +@st.composite +@settings() +def valid_range_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + + # Input type consistency + all_valid_types = list(range_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + input_type = draw(st.sampled_from(all_valid_types)) + input_dtype = range_types.get(inputs_attributes["ONNXRuntime_Provider"])[input_type] + s_l_strategy = None + d_strategy = None + if np.issubdtype(input_dtype, np.integer): + min_value = np.iinfo(input_dtype).min + max_value = np.iinfo(input_dtype).max + min_value = max(min_value, inputs_attributes["min_input_int"]) + max_value = min(max_value, inputs_attributes["max_input_int"]) + s_l_strategy = st.integers(min_value=min_value, max_value=max_value) + + # D [C1] + d_strategy = st.one_of( + st.integers(min_value=min_value, max_value=-1), + st.integers(min_value=1, max_value=max_value) + ) + elif np.issubdtype(input_dtype, np.floating): + min_value = np.finfo(input_dtype).min + max_value = np.finfo(input_dtype).max + min_value = max(min_value, inputs_attributes["min_input_float"]) + max_value = min(max_value, inputs_attributes["max_input_float"]) + s_l_strategy = st.floats(min_value= min_value, max_value=max_value) + + # D [C1] + eps = np.finfo(input_dtype).eps + d_strategy = st.one_of( + st.floats(min_value=min_value, max_value= -eps), + st.floats(min_value=eps, max_value=max_value) + ) + + #--------------------------------------------------- + # Input S (Start) + #--------------------------------------------------- + s = draw(hnp.arrays(dtype=input_dtype, shape=[], elements=s_l_strategy)) + + #--------------------------------------------------- + # Input L (Limit) + #--------------------------------------------------- + l = draw(hnp.arrays(dtype=input_dtype, shape=[], elements=s_l_strategy)) + + #--------------------------------------------------- + # Input D (Delta) + #--------------------------------------------------- + d = draw(hnp.arrays(dtype=input_dtype, shape=[], elements=d_strategy)) + + y_shape = calculate_y_shape(s.item(), l.item(), d.item()) + + # Ensures that the output shape does not overflow memory limits + assume (y_shape <= inputs_attributes["max_output_elements"]) + + return s, l, d, input_dtype, y_shape + + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_range_args()) +def test_range(args): + s, l, d, input_dtype, y_shape = args + generated_data["s_tensor"].append(s) + generated_data["l_tensor"].append(l) + generated_data["d_tensor"].append(d) + input_type_key = dtype_to_key.get(input_dtype, str(input_dtype)) + generated_data["input_type"].append(input_type_key) + y = run_onnx_range_test(s, l, d, inputs_attributes["ONNXRuntime_Provider"]) + check_constraints(d, y, y_shape) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + s_tensor_int = [s.tolist() for s, t in + zip(generated_data["s_tensor"], generated_data["input_type"]) + if "INT" in t] + s_tensor_float =[s.tolist() for s, t in + zip(generated_data["s_tensor"], generated_data["input_type"]) + if "FP" in t] + l_tensor_int =[l.tolist() for l, t in + zip(generated_data["l_tensor"], generated_data["input_type"]) + if "INT" in t] + l_tensor_float =[l.tolist() for l, t in + zip(generated_data["l_tensor"], generated_data["input_type"]) + if "FP" in t] + d_tensor_int =[d.tolist() for d, t in + zip(generated_data["d_tensor"], generated_data["input_type"]) + if "INT" in t] + d_tensor_float =[d.tolist() for d, t in + zip(generated_data["d_tensor"], generated_data["input_type"]) + if "FP" in t] + + data = { + "title": "Data generated for testing ONNX Range Operator", + "min_input_int": inputs_attributes["min_input_int"], + "max_input_int": inputs_attributes["max_input_int"], + "min_input_float": inputs_attributes["min_input_float"], + "max_input_float": inputs_attributes["max_input_float"], + "s_tensor_int": s_tensor_int, + "s_tensor_float": s_tensor_float, + "l_tensor_int": l_tensor_int, + "l_tensor_float": l_tensor_float, + "d_tensor_int": d_tensor_int, + "d_tensor_float": d_tensor_float, + "input_type": generated_data["input_type"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + + +def run_onnx_range_test(s, l, d, provider): + """ + Function that runs the ONNX Range operation + """ + s_onnx = helper.make_tensor_value_info('s', helper.np_dtype_to_tensor_dtype(s.dtype), []) + l_onnx = helper.make_tensor_value_info('l', helper.np_dtype_to_tensor_dtype(l.dtype), []) + d_onnx = helper.make_tensor_value_info('d', helper.np_dtype_to_tensor_dtype(d.dtype), []) + + # Dynamic output shape ([None]), because float operations can lead to imprecisions + # Due to the roundings (on float datatypes) the y_dimensions calculation may not be exact + # Thus, we set the output shape to None to avoid shape mismatch errors + y_onnx = helper.make_tensor_value_info('y', helper.np_dtype_to_tensor_dtype(s.dtype), [None]) + + node_def = helper.make_node( + 'Range', + inputs=['s', 'l', 'd'], + outputs=['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test_range', + [s_onnx, l_onnx, d_onnx], + [y_onnx], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 11 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'s': s, 'l': l, 'd': d})[0] + print("y shape:", y.shape) + print("y dtype:", y.dtype) + print("y:", y) + return y + +def check_constraints(d, y, y_shape): + # Cant check this assert because of numerical precision issues with floats + # assert list (y.shape) == [y_shape] + # Instead, we will say that the dimensions (expected/real) differ by at most 1 + assert abs(y.shape[0] - y_shape) <= 1 + # D [C1] + assert d.item() != 0 diff --git a/safety-related-profile/sonnx/ops/spec/tests/range/range.ipynb b/safety-related-profile/sonnx/ops/spec/tests/range/range.ipynb new file mode 100644 index 00000000..9df4d251 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/range/range.ipynb @@ -0,0 +1,323 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "02a475ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/jcm-machado/venv/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/jcm-machado/venv/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/jcm-machado/venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "import math\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"S\"\n", + "input2_name = \"L\"\n", + "input3_name = \"D\"\n", + "\n", + "range_output_name = \"Y\"\n", + "\n", + "\n", + "# Calculates the total number of elements of the inteneded range\n", + "def num_elements(S, L, D):\n", + " if D == 0:\n", + " raise ValueError(\"Step (D) cannot be zero.\")\n", + " return max(math.ceil((L - S) / D), 0)\n", + "\n", + "# Create the ONNX model with Reshape operator\n", + "def create_reshape_model(out_shape, dtype):\n", + " # Create \"S\" input tensor (scalar tensor)\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, [])\n", + " # Create \"L\" input tensor (scalar tensor)\n", + " input2 = onnx.helper.make_tensor_value_info(input2_name, dtype, [])\n", + " # Create \"D\" input tensor (scalar tensor) \n", + " input3 = onnx.helper.make_tensor_value_info(input3_name, dtype, [])\n", + " # Create output tensor (final result after range operation)\n", + " range_output = onnx.helper.make_tensor_value_info(range_output_name, dtype, out_shape)\n", + "\n", + " # Define range node\n", + " range_node = onnx.helper.make_node(\n", + " \"Range\",\n", + " inputs=[input1_name, input2_name, input3_name],\n", + " outputs=[range_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph (no output shape specified - will be inferred at runtime)\n", + " graph_def = onnx.helper.make_graph(\n", + " [range_node],\n", + " \"Range\",\n", + " [input1, input2, input3],\n", + " [range_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 11)])\n", + " model.ir_version = 10\n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"range.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"range.onnx\")\n", + " return session\n", + "\n", + "def do_range(S, L, D, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: S, input2_name: L, input3_name: D})\n", + "\n", + " S_f = (np.array2string(S, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " L_f = (np.array2string(L, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " D_f = (np.array2string(D, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"S shape: {S.shape}, X={S_f}\")\n", + " print(f\"L shape: {L.shape}, X={L_f}\")\n", + " print(f\"D shape: {D.shape}, X={D_f}\")\n", + " print(f\"Output shape: {output[0].shape}, Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Simple cases" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S shape: (), X=0\n", + "L shape: (), X=10\n", + "D shape: (), X=1\n", + "Output shape: (10,), Result = [0,1,2,3,4,5,6,7,8,9]\n" + ] + } + ], + "source": [ + "# Case N1 - ordered bounds and positive delta\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(0, dtype=np.int32)\n", + "L = np.array(10, dtype=np.int32)\n", + "D = np.array(1, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "8c520fe5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S shape: (), X=0\n", + "L shape: (), X=10\n", + "D shape: (), X=-1\n", + "Output shape: (0,), Result = []\n" + ] + } + ], + "source": [ + "# Case N2 - ordered bounds and negative delta\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(0, dtype=np.int32)\n", + "L = np.array(10, dtype=np.int32)\n", + "D = np.array(-1, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "712cb415", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S shape: (), X=10\n", + "L shape: (), X=0\n", + "D shape: (), X=1\n", + "Output shape: (0,), Result = []\n" + ] + } + ], + "source": [ + "# Case N3 - inverted bounds and positive delta\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(10, dtype=np.int32)\n", + "L = np.array(0, dtype=np.int32)\n", + "D = np.array(1, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "d602bfa8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S shape: (), X=10\n", + "L shape: (), X=0\n", + "D shape: (), X=-1\n", + "Output shape: (10,), Result = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\n" + ] + } + ], + "source": [ + "# Case N4 - inverted bounds and negative delta\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(10, dtype=np.int32)\n", + "L = np.array(0, dtype=np.int32)\n", + "D = np.array(-1, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c4a0ffb0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "S shape: (), X=3\n", + "L shape: (), X=3\n", + "D shape: (), X=1\n", + "Output shape: (0,), Result = []\n" + ] + } + ], + "source": [ + "# Case N5 - S equals L\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(3, dtype=np.int32)\n", + "L = np.array(3, dtype=np.int32)\n", + "D = np.array(1, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fbc5635b", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Step (D) cannot be zero.", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mValueError\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[8]\u001b[39m\u001b[32m, line 8\u001b[39m\n\u001b[32m 6\u001b[39m L = np.array(\u001b[32m3\u001b[39m, dtype=np.int32)\n\u001b[32m 7\u001b[39m D = np.array(\u001b[32m0\u001b[39m, dtype=np.int32)\n\u001b[32m----> \u001b[39m\u001b[32m8\u001b[39m n_values = \u001b[43mnum_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mS\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mL\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mD\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 9\u001b[39m session = create_reshape_model([n_values], onnx_type)\n\u001b[32m 10\u001b[39m do_range(S, L, D, session)\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 17\u001b[39m, in \u001b[36mnum_elements\u001b[39m\u001b[34m(S, L, D)\u001b[39m\n\u001b[32m 15\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mnum_elements\u001b[39m(S, L, D):\n\u001b[32m 16\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m D == \u001b[32m0\u001b[39m:\n\u001b[32m---> \u001b[39m\u001b[32m17\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m\"\u001b[39m\u001b[33mStep (D) cannot be zero.\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mmax\u001b[39m(math.ceil((L - S) / D), \u001b[32m0\u001b[39m)\n", + "\u001b[31mValueError\u001b[39m: Step (D) cannot be zero." + ] + } + ], + "source": [ + "# This cell MUST FAIL!\n", + "# Case N5 - D equals zero\n", + "# Input tensors: S, L, D (scalar tensors)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "S = np.array(3, dtype=np.int32)\n", + "L = np.array(3, dtype=np.int32)\n", + "D = np.array(0, dtype=np.int32)\n", + "n_values = num_elements(S, L, D)\n", + "session = create_reshape_model([n_values], onnx_type)\n", + "do_range(S, L, D, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python (Hypothesis venv)", + "language": "python", + "name": "hypothesis-env" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/relu/README.md b/safety-related-profile/sonnx/ops/spec/tests/relu/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/check_edges.py new file mode 100644 index 00000000..fd419c97 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/check_edges.py @@ -0,0 +1,130 @@ +""" +This file checks edge cases in the generated data for the Reshape operation in ONNX. +""" +import json +import numpy as np +import ml_dtypes + +""" +Reshape supported types, organized by ONNXRuntime_Provider +""" +reshape_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + check_rank_input_x = check_individual_variables( + "rank_input_tensor_x", data["rank_input_tensor_x"], + data["min_rank_input"], data["max_rank_input"]) + + check_rank_input_s = check_individual_variables( + "rank_input_tensor_s", data["rank_input_tensor_s"], + data["min_rank_input"], data["max_rank_input"]) + + check_shape_size_input_x = check_individual_variables( + "shape_input_tensor_x", + [size for shape in data["shape_input_tensor_x"] for size in shape], + data["min_dim_size_input"], data["max_dim_size_input"]) + + check_shape_size_input_s = check_individual_variables( + "shape_input_tensor_s", + [size for shape in data["shape_input_tensor_s"] for size in shape], + data["min_dim_size_input"], data["max_dim_size_input"]) + + provider = data["ONNXRuntime_Provider"] + + check_input_type = check_type("input_type", data["input_type"], provider) + + return all([check_rank_input_x, check_rank_input_s, check_shape_size_input_x, check_shape_size_input_s, check_input_type]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(reshape_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + + +print(check_edges()) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/generated_data.json b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/generated_data.json new file mode 100644 index 00000000..89dece6d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/generated_data.json @@ -0,0 +1,554072 @@ +{ + "title": "Data generated by Hypothesis for Reshape operation tests", + "min_rank_input": 1, + "max_rank_input": 5, + "rank_input_tensor_x": [ + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 4, + 3, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 2, + 5, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 2, + 3, + 4, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 1, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 1, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 3, + 3, + 3, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 2, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 3, + 3, + 3, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 4, + 4, + 4, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 1, + 1, + 1, + 5, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 4, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 4, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 1, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 4, + 4, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 2, + 2, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 4, + 4, + 4, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 2, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 1, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 1, + 1, + 4, + 4, + 4, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 2, + 2, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 2, + 2, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 3, + 1, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 1, + 1, + 1, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 4, + 1, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + 5, + 5, + 5, + 5, + 5, + 3, + 4, + 4, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 3, + 3, + 3, + 3, + 3 + ], + "rank_input_tensor_s": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ], + "min_dim_size_input": 1, + "max_dim_size_input": 5, + "shape_input_tensor_x": [ + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 3, + 2, + 5 + ], + [ + 4, + 2, + 3, + 2, + 5 + ], + [ + 4, + 2, + 1, + 3, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1, + 2, + 1, + 2 + ], + [ + 4, + 1, + 2, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 4 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 4, + 2 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 4, + 4, + 1, + 2, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1, + 2, + 4 + ], + [ + 2, + 4, + 1, + 2, + 4 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 5, + 1 + ], + [ + 5, + 1, + 3, + 5, + 1 + ], + [ + 5, + 1, + 3, + 5, + 1 + ], + [ + 5, + 1, + 3, + 5, + 1 + ], + [ + 5, + 1, + 3, + 5, + 1 + ], + [ + 5, + 1, + 3, + 3, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 3, + 3 + ], + [ + 4, + 1, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 5 + ], + [ + 3, + 2, + 3, + 1, + 5 + ], + [ + 3, + 2, + 3, + 1, + 5 + ], + [ + 3, + 2, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5, + 2, + 3, + 3 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 5, + 3, + 3 + ], + [ + 3, + 2, + 5, + 3, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 4, + 3, + 3 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 2 + ], + [ + 2, + 5, + 1, + 2 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 4, + 1, + 4 + ], + [ + 4, + 2, + 4, + 1, + 4 + ], + [ + 4, + 2, + 4, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 5, + 1, + 2, + 4, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 2 + ], + [ + 4, + 3, + 5, + 2 + ], + [ + 3, + 5, + 4, + 3 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 1, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 4, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 5, + 1, + 5, + 5, + 1 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 5, + 4, + 1, + 2, + 1 + ], + [ + 5, + 4, + 1, + 2, + 1 + ], + [ + 5, + 4, + 1, + 2, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 5, + 3, + 2, + 4, + 5 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 1, + 2, + 5, + 5, + 5 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 5, + 3, + 2, + 5 + ], + [ + 3, + 5, + 3, + 2, + 5 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2, + 4, + 3, + 4 + ], + [ + 4, + 2, + 4, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 4, + 2, + 4, + 5, + 5 + ], + [ + 4, + 2, + 4, + 5, + 5 + ], + [ + 4, + 2, + 4, + 4, + 5 + ], + [ + 5, + 2, + 4, + 4, + 5 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 4, + 1, + 2 + ], + [ + 5, + 1, + 4, + 1, + 2 + ], + [ + 2, + 1, + 4, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 4, + 2, + 2 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 4, + 1, + 3 + ], + [ + 1, + 3, + 4, + 1, + 3 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 5 + ], + [ + 3, + 3, + 2, + 5, + 3 + ], + [ + 3, + 3, + 2, + 5, + 3 + ], + [ + 3, + 3, + 2, + 5, + 3 + ], + [ + 3, + 3, + 2, + 5, + 5 + ], + [ + 3, + 3, + 2, + 5, + 5 + ], + [ + 3, + 3, + 2, + 5, + 5 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 3, + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 2, + 4, + 2, + 2 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 3, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 2, + 3, + 3, + 1 + ], + [ + 1, + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 3, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3, + 4, + 5 + ], + [ + 3, + 2, + 3, + 4, + 5 + ], + [ + 3, + 2, + 2, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2 + ], + [ + 4, + 2, + 4, + 2 + ], + [ + 4, + 2, + 4, + 2 + ], + [ + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3, + 4, + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 3, + 4, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 2, + 4, + 3, + 2, + 2 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 1, + 5, + 3, + 1 + ], + [ + 2, + 1, + 5, + 3, + 1 + ], + [ + 2, + 1, + 5, + 3, + 1 + ], + [ + 2, + 1, + 5, + 3, + 1 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 4 + ], + [ + 3, + 3, + 2, + 1, + 4 + ], + [ + 3, + 3, + 2, + 1, + 4 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 3, + 2 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + 3, + 3, + 4, + 3 + ], + [ + 5, + 3, + 3, + 4, + 3 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 5, + 4, + 3, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 2, + 5, + 4, + 4 + ], + [ + 2, + 2, + 5, + 4, + 4 + ], + [ + 2, + 2, + 5, + 4, + 4 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 4, + 2, + 1, + 5, + 3 + ], + [ + 4, + 2, + 1, + 5, + 3 + ], + [ + 4, + 2, + 1, + 5, + 3 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 4, + 1, + 5, + 1, + 4 + ], + [ + 4, + 1, + 5, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 3, + 5, + 4 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 5, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 5, + 3, + 4, + 3, + 5 + ], + [ + 5, + 3, + 4, + 3, + 5 + ], + [ + 5, + 3, + 4, + 3, + 5 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 5 + ], + [ + 5, + 3, + 4, + 3, + 5 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 2 + ], + [ + 2, + 5, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 5, + 4, + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 3, + 5, + 5 + ], + [ + 5, + 4, + 3, + 5, + 5 + ], + [ + 5, + 4, + 3, + 5, + 5 + ], + [ + 5, + 4, + 3, + 5, + 5 + ], + [ + 5, + 4, + 3, + 5, + 5 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 2, + 5, + 5, + 2 + ], + [ + 2, + 5, + 5, + 2 + ], + [ + 2, + 5, + 5, + 2 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 2, + 1 + ], + [ + 5, + 5, + 1, + 2, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 3, + 3, + 4, + 5, + 3 + ], + [ + 3, + 3, + 4, + 5, + 3 + ], + [ + 3, + 3, + 4, + 5, + 3 + ], + [ + 3, + 3, + 4, + 5, + 3 + ], + [ + 3, + 3, + 4, + 5, + 3 + ], + [ + 3, + 1, + 3, + 4, + 5 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 2, + 2, + 3 + ], + [ + 1, + 1, + 2, + 2, + 3 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 1, + 2, + 3, + 1, + 3 + ], + [ + 1, + 2, + 3, + 1, + 3 + ], + [ + 1, + 2, + 3, + 1, + 3 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 2, + 5, + 1 + ], + [ + 4, + 2, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 2, + 3, + 2, + 1, + 3 + ], + [ + 2, + 3, + 2, + 1, + 3 + ], + [ + 2, + 3, + 2, + 1, + 3 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 5 + ], + [ + 2, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 4, + 3, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5, + 5 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 4, + 4, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 2, + 5 + ], + [ + 5, + 3, + 2, + 5 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 2, + 4, + 3, + 2, + 5 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 4, + 1 + ], + [ + 3, + 3, + 2, + 4, + 1 + ], + [ + 3, + 3, + 2, + 4, + 1 + ], + [ + 3, + 3, + 2, + 4, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 2 + ], + [ + 3, + 2, + 4, + 2 + ], + [ + 3, + 2, + 4, + 2 + ], + [ + 1 + ], + [ + 4, + 5, + 4, + 2, + 5 + ], + [ + 4, + 5, + 4, + 2, + 5 + ], + [ + 4, + 5, + 4, + 2, + 5 + ], + [ + 4, + 5, + 4, + 2, + 5 + ], + [ + 4, + 5, + 4, + 2, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 1, + 3, + 3 + ], + [ + 3, + 2, + 1, + 3, + 3 + ], + [ + 3, + 2, + 1, + 5, + 4 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 1, + 3 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 3, + 5, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 3, + 3 + ], + [ + 3, + 3, + 1, + 3, + 3 + ], + [ + 3, + 3, + 1, + 3, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 1, + 5, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 4, + 3, + 3, + 2 + ], + [ + 4, + 3, + 3, + 2 + ], + [ + 4, + 3, + 3, + 2 + ], + [ + 4, + 3, + 3, + 2 + ], + [ + 4, + 3, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2, + 4 + ], + [ + 3, + 2, + 3, + 2, + 4 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 2 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 2, + 1, + 3 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 2, + 5 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 3 + ], + [ + 3, + 1, + 2, + 1, + 3 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 3, + 3, + 1 + ], + [ + 1, + 2, + 3, + 3, + 1 + ], + [ + 1, + 2, + 3, + 3, + 1 + ], + [ + 1, + 1, + 2, + 1, + 5 + ], + [ + 1, + 1, + 2, + 1, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 4, + 5 + ], + [ + 3, + 4, + 1, + 2, + 3 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 2, + 4, + 1, + 4, + 3 + ], + [ + 5, + 5, + 4, + 2, + 5 + ], + [ + 5, + 5, + 4, + 2, + 5 + ], + [ + 5, + 5, + 4, + 2, + 5 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 3, + 5, + 5, + 3, + 1 + ], + [ + 3, + 5, + 5, + 3, + 1 + ], + [ + 3, + 5, + 5, + 3, + 1 + ], + [ + 3, + 5, + 5, + 3, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 4, + 3, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 4, + 2, + 1, + 3 + ], + [ + 5, + 4, + 2, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 5, + 1, + 5 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 2, + 1, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 2, + 4, + 3 + ], + [ + 2, + 4, + 2, + 4, + 3 + ], + [ + 2, + 4, + 2, + 4, + 3 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1, + 3, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 1, + 5, + 4 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 5, + 1, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 4, + 2, + 5, + 2 + ], + [ + 4, + 2, + 5, + 2 + ], + [ + 4, + 2, + 5, + 2 + ], + [ + 4, + 2, + 5, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 3, + 4 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 2, + 3 + ], + [ + 1, + 1, + 5, + 2, + 3 + ], + [ + 1, + 1, + 5, + 2, + 3 + ], + [ + 1, + 1, + 5, + 2, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 5, + 5, + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 1, + 5, + 2, + 3, + 3 + ], + [ + 1, + 5, + 2, + 3, + 1 + ], + [ + 1, + 5, + 2, + 3, + 1 + ], + [ + 1, + 5, + 2, + 3, + 1 + ], + [ + 1, + 5, + 2, + 5, + 2 + ], + [ + 1, + 5, + 1, + 2, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 4, + 2 + ], + [ + 4, + 5, + 4, + 2 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2, + 4, + 2, + 4 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 3, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 5, + 5, + 3 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 3, + 2, + 4, + 3 + ], + [ + 3, + 3, + 2, + 4, + 3 + ], + [ + 3, + 3, + 2, + 4, + 3 + ], + [ + 3, + 3, + 2, + 4, + 3 + ], + [ + 3, + 3, + 2, + 4, + 3 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 2, + 2, + 4 + ], + [ + 3, + 5, + 2, + 1, + 1 + ], + [ + 3, + 5, + 2, + 1, + 1 + ], + [ + 3, + 5, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 3, + 3 + ], + [ + 1, + 1, + 3, + 1, + 3 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 1, + 1, + 2, + 2, + 5 + ], + [ + 1, + 1, + 2, + 2, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2, + 5, + 4, + 3 + ], + [ + 2, + 2, + 5, + 4, + 3 + ], + [ + 2, + 2, + 5, + 4, + 3 + ], + [ + 2, + 2, + 5, + 4, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 5, + 5, + 3 + ], + [ + 1, + 1, + 5, + 5, + 3 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 2, + 1 + ], + [ + 3, + 1, + 1, + 3, + 2 + ], + [ + 3 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 4, + 3 + ], + [ + 3, + 1, + 4, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 2, + 4, + 2 + ], + [ + 1, + 2, + 4, + 2 + ], + [ + 1, + 2, + 4, + 2 + ], + [ + 1, + 2, + 4, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 3, + 5, + 2, + 5, + 3 + ], + [ + 3, + 5, + 2, + 5, + 3 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 2, + 4, + 2, + 4, + 4 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 4, + 5 + ], + [ + 1, + 2, + 1, + 4, + 5 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 5, + 4, + 5, + 2 + ], + [ + 5, + 4, + 5, + 2 + ], + [ + 5, + 4, + 5, + 2 + ], + [ + 5, + 4, + 5, + 2 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 4, + 2, + 5, + 5, + 1 + ], + [ + 4, + 2, + 5, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 1, + 5, + 1, + 5 + ], + [ + 1, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 4, + 3, + 4 + ], + [ + 1, + 4, + 4, + 3, + 1 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4, + 4, + 3, + 3 + ], + [ + 4, + 4, + 3, + 3 + ], + [ + 4, + 4, + 3, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 1, + 4, + 2 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 3, + 4, + 5, + 4 + ], + [ + 5, + 3, + 4, + 1, + 1 + ], + [ + 5, + 3, + 4, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 4, + 2, + 3, + 2 + ], + [ + 3, + 4, + 2, + 3, + 2 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 2, + 4, + 4, + 4 + ], + [ + 1, + 2, + 4, + 4, + 4 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 2 + ], + [ + 1, + 5, + 3, + 2 + ], + [ + 1, + 5, + 1, + 2 + ], + [ + 1, + 5, + 5, + 2 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 3, + 4, + 3, + 3, + 5 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 2 + ], + [ + 3, + 4, + 3, + 3, + 2 + ], + [ + 3, + 4, + 3, + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 4, + 2, + 3 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 5 + ], + [ + 5, + 4, + 4, + 5 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 2, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 4, + 2 + ], + [ + 3, + 3, + 2, + 2, + 1 + ], + [ + 3, + 3, + 2, + 2, + 1 + ], + [ + 3, + 3, + 2, + 2, + 1 + ], + [ + 3, + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 5, + 4, + 4 + ], + [ + 3, + 2, + 5, + 4, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 4, + 2, + 2, + 5, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 2, + 3 + ], + [ + 2, + 2, + 3, + 2, + 3 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 2 + ], + [ + 1, + 4, + 2, + 1, + 2 + ], + [ + 1, + 4, + 2, + 1, + 2 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 2, + 3 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 4, + 1, + 5 + ], + [ + 1, + 5, + 1, + 5, + 5 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 4, + 5, + 4, + 4, + 3 + ], + [ + 4, + 5, + 4, + 4, + 3 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 4 + ], + [ + 5, + 1, + 2, + 1, + 4 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 4, + 1 + ], + [ + 5, + 1, + 2, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 5, + 2, + 5 + ], + [ + 4, + 1, + 5, + 2, + 5 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 3 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 2, + 3, + 1, + 3, + 5 + ], + [ + 2, + 3, + 1, + 3, + 5 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 3, + 5 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 3, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 4, + 3 + ], + [ + 3, + 2, + 4, + 4, + 3 + ], + [ + 3, + 2, + 4, + 4, + 1 + ], + [ + 3, + 2, + 4, + 4, + 1 + ], + [ + 3, + 2, + 4, + 4, + 2 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 1, + 2 + ], + [ + 5, + 4, + 2, + 4, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 1, + 2, + 1 + ], + [ + 4, + 4, + 1, + 2, + 1 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4, + 4 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4, + 3, + 5, + 3 + ], + [ + 2, + 4, + 3, + 5, + 3 + ], + [ + 2, + 4, + 3, + 5, + 3 + ], + [ + 2, + 4, + 3, + 5, + 3 + ], + [ + 2, + 4, + 3, + 5, + 1 + ], + [ + 2, + 4, + 1, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 2, + 2 + ], + [ + 4, + 5, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 5 + ], + [ + 3, + 1, + 1, + 4, + 5 + ], + [ + 3, + 1, + 1, + 4, + 5 + ], + [ + 3, + 1, + 1, + 4, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 5, + 1, + 4, + 2, + 5 + ], + [ + 5, + 1, + 4, + 2, + 5 + ], + [ + 5, + 1, + 4, + 2, + 5 + ], + [ + 5, + 1, + 4, + 2, + 5 + ], + [ + 5, + 1, + 4, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 4, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 2, + 5, + 5 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 3, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 4, + 3, + 2 + ], + [ + 2, + 2, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1, + 3, + 2, + 5 + ], + [ + 1, + 3, + 2, + 5 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 3, + 1, + 3, + 5, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 5, + 4, + 2 + ], + [ + 2, + 4, + 5, + 4, + 2 + ], + [ + 2, + 4, + 5, + 4, + 2 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2, + 2, + 2 + ], + [ + 3, + 2, + 2, + 2 + ], + [ + 3, + 2, + 2, + 2 + ], + [ + 3, + 2, + 2, + 2 + ], + [ + 3, + 2, + 2, + 2 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 5, + 3, + 5, + 3 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 5, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 5, + 4, + 3 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 1 + ], + [ + 5, + 3, + 4, + 5, + 5 + ], + [ + 5, + 3, + 4, + 5, + 5 + ], + [ + 5, + 3, + 4, + 5, + 5 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 3, + 2, + 3, + 3 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 5, + 2 + ], + [ + 5, + 5, + 2, + 5, + 2 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 2, + 4, + 3 + ], + [ + 4, + 2, + 2, + 4, + 3 + ], + [ + 4, + 2, + 2, + 5, + 3 + ], + [ + 4, + 2, + 2, + 5, + 3 + ], + [ + 4, + 2, + 2, + 5, + 3 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 3, + 3, + 2 + ], + [ + 2, + 5, + 3, + 3, + 2 + ], + [ + 2, + 5, + 3, + 3, + 2 + ], + [ + 2, + 5, + 3, + 3, + 2 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 5, + 2, + 3, + 3 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 4, + 2 + ], + [ + 2, + 1, + 2, + 4, + 2 + ], + [ + 2, + 1, + 2, + 4, + 2 + ], + [ + 2, + 1, + 2, + 4, + 2 + ], + [ + 2, + 1, + 2, + 4, + 1 + ], + [ + 2, + 1, + 1, + 2, + 4 + ], + [ + 2, + 1, + 1, + 2, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3, + 5 + ], + [ + 1, + 2, + 1, + 3, + 5 + ], + [ + 1, + 2, + 1, + 3, + 5 + ], + [ + 1, + 2, + 1, + 2, + 2 + ], + [ + 1, + 2, + 1, + 2, + 2 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 5, + 3, + 3 + ], + [ + 2, + 2, + 5, + 3, + 3 + ], + [ + 2, + 2, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 4, + 4, + 2, + 2, + 4 + ], + [ + 4, + 4, + 2, + 2, + 4 + ], + [ + 4, + 4, + 2, + 2, + 1 + ], + [ + 4, + 4, + 2, + 2, + 1 + ], + [ + 4, + 4, + 2, + 2, + 1 + ], + [ + 4, + 4, + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 2, + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3, + 3, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 4 + ], + [ + 4, + 3, + 4, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 2, + 2, + 3, + 4, + 3 + ], + [ + 2, + 2, + 3, + 5, + 1 + ], + [ + 2, + 1, + 2, + 3, + 5 + ], + [ + 2, + 1, + 2, + 3, + 1 + ], + [ + 2, + 1, + 2, + 3, + 1 + ], + [ + 2, + 1, + 2, + 3, + 1 + ], + [ + 2, + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 5, + 5, + 4 + ], + [ + 5, + 2, + 5, + 5, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 4 + ], + [ + 1, + 4, + 2, + 4 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 2, + 4, + 4, + 4, + 5 + ], + [ + 2, + 4, + 4, + 4, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 1, + 2, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 4, + 4 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 4, + 2, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 2, + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 5, + 5 + ], + [ + 3, + 4, + 1, + 5, + 5 + ], + [ + 3, + 4, + 1, + 5, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 5, + 5, + 4, + 4, + 3 + ], + [ + 5, + 5, + 4, + 4, + 3 + ], + [ + 5, + 5, + 4, + 4, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 2, + 2, + 2 + ], + [ + 2, + 4, + 2, + 2, + 2 + ], + [ + 2, + 4, + 2, + 2, + 2 + ], + [ + 2, + 4, + 2, + 2, + 2 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 1, + 4, + 5, + 2, + 2 + ], + [ + 1, + 4, + 5, + 2, + 2 + ], + [ + 1, + 4, + 3, + 5, + 2 + ], + [ + 1, + 4, + 3, + 5, + 2 + ], + [ + 1, + 4, + 3, + 5, + 2 + ], + [ + 1, + 4, + 3, + 5, + 2 + ], + [ + 1, + 5, + 3, + 5, + 2 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 2, + 5, + 4 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 2, + 3, + 2 + ], + [ + 4, + 1, + 2, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 3, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 1, + 4, + 2 + ], + [ + 4, + 3, + 1, + 4, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 2 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 2 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 5, + 3, + 2 + ], + [ + 5, + 5, + 3, + 2 + ], + [ + 5, + 5, + 3, + 2 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2, + 4, + 4 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 5, + 3, + 2 + ], + [ + 4, + 4, + 5, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 1, + 3, + 5, + 2, + 4 + ], + [ + 1, + 3, + 5, + 2, + 4 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 1, + 2, + 2 + ], + [ + 3, + 2, + 1, + 2, + 2 + ], + [ + 3, + 2, + 1, + 2, + 2 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 2, + 3, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 5, + 5, + 5 + ], + [ + 3, + 2, + 5, + 5, + 5 + ], + [ + 3, + 2, + 5, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 2, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 2, + 4, + 3 + ], + [ + 4, + 2, + 4, + 3 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 5 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 3, + 4, + 3 + ], + [ + 5, + 2, + 3, + 4, + 3 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3, + 1, + 2 + ], + [ + 4, + 5, + 3, + 1, + 2 + ], + [ + 4, + 5, + 3, + 1, + 2 + ], + [ + 4, + 5, + 3, + 1, + 2 + ], + [ + 4, + 5, + 3, + 1, + 2 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 3, + 4, + 4 + ], + [ + 1, + 3, + 3, + 4, + 4 + ], + [ + 1, + 3, + 3, + 4, + 4 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 5, + 3, + 2, + 4 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 2 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 4, + 3 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 3, + 4, + 4 + ], + [ + 2, + 2, + 3, + 4, + 4 + ], + [ + 2, + 2, + 3, + 4, + 4 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 3, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 4, + 3 + ], + [ + 1, + 1, + 3, + 4, + 3 + ], + [ + 1, + 1, + 3, + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 2, + 3, + 4, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 2, + 3, + 1 + ], + [ + 5, + 4, + 2, + 3, + 1 + ], + [ + 5, + 4, + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 4, + 3, + 5, + 1, + 3 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 5, + 2, + 1, + 5 + ], + [ + 4, + 5, + 2, + 1, + 5 + ], + [ + 4, + 5, + 2, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 3, + 4, + 1, + 2, + 2 + ], + [ + 3, + 4, + 1, + 2, + 2 + ], + [ + 3, + 4, + 1, + 2, + 2 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 5, + 2, + 2 + ], + [ + 4, + 3, + 5, + 2, + 2 + ], + [ + 4, + 3, + 5, + 2, + 2 + ], + [ + 4, + 3, + 5, + 2, + 2 + ], + [ + 4, + 3, + 5, + 2, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 1, + 3, + 4, + 1, + 3 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 4, + 4, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 1, + 2, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 4, + 1, + 2 + ], + [ + 5, + 1, + 1, + 3, + 3 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 2 + ], + [ + 3, + 1, + 3, + 2 + ], + [ + 3, + 1, + 3, + 2 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 2, + 2, + 3 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 1, + 3, + 2 + ], + [ + 4, + 4, + 1, + 3, + 2 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 4, + 1, + 4, + 1 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 2 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1, + 2, + 4 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 5, + 1, + 4 + ], + [ + 2, + 1, + 5, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4, + 3 + ], + [ + 3, + 4, + 2, + 4, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 4 + ], + [ + 3, + 4, + 2, + 1, + 4 + ], + [ + 3, + 4, + 2, + 1, + 4 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 1, + 2, + 2, + 4, + 5 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 4, + 5, + 2 + ], + [ + 3, + 4, + 5, + 2 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 2, + 3, + 5 + ], + [ + 4, + 5, + 2, + 3, + 5 + ], + [ + 4, + 5, + 2, + 3, + 5 + ], + [ + 4, + 5, + 2, + 3, + 5 + ], + [ + 4, + 5, + 2, + 3, + 5 + ], + [ + 4, + 5, + 2, + 3, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 4, + 3, + 3, + 4 + ], + [ + 5, + 4, + 3, + 3, + 4 + ], + [ + 5, + 4, + 3, + 3, + 4 + ], + [ + 5, + 4, + 3, + 3, + 4 + ], + [ + 5, + 4, + 3, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 2, + 2, + 3, + 3, + 1 + ], + [ + 2, + 2, + 3, + 3, + 1 + ], + [ + 2, + 2, + 3, + 3, + 1 + ], + [ + 2, + 2, + 3, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 5, + 5, + 2 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 2, + 2, + 5 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 3, + 3, + 3 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 5 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 2, + 5, + 2, + 3, + 3 + ], + [ + 2, + 5, + 2, + 3, + 3 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3, + 2, + 4 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 4 + ], + [ + 2, + 1, + 3, + 1, + 4 + ], + [ + 2, + 1, + 3, + 1, + 4 + ], + [ + 2, + 1, + 3, + 1, + 4 + ], + [ + 2, + 1, + 3, + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 3, + 4, + 4, + 2 + ], + [ + 1, + 3, + 4, + 4, + 2 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 4, + 3 + ], + [ + 2, + 1, + 4, + 4, + 3 + ], + [ + 2, + 1, + 4, + 4, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5, + 2 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 5, + 2, + 3, + 3, + 3 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 1, + 3, + 1 + ], + [ + 5, + 2, + 1, + 3, + 1 + ], + [ + 5, + 1, + 2, + 1, + 3 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 5, + 2, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 3, + 4, + 3 + ], + [ + 2, + 3, + 3, + 4, + 3 + ], + [ + 2, + 3, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 3, + 3 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 3, + 1 + ], + [ + 5, + 5, + 4, + 3, + 1 + ], + [ + 5, + 5, + 4, + 3, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 3, + 1, + 5 + ], + [ + 4, + 4, + 3, + 1, + 5 + ], + [ + 4, + 4, + 3, + 1, + 5 + ], + [ + 4, + 4, + 3, + 1, + 5 + ], + [ + 4, + 4, + 3, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 5 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 4, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 3, + 2, + 2 + ], + [ + 5, + 3, + 2, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 1, + 5 + ], + [ + 4, + 2, + 1, + 5 + ], + [ + 4, + 2, + 1, + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 2, + 3 + ], + [ + 2, + 1, + 2, + 3 + ], + [ + 2, + 1, + 2, + 3 + ], + [ + 2, + 1, + 2, + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 5, + 1 + ], + [ + 4, + 4, + 3, + 5, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 2, + 1, + 5 + ], + [ + 1, + 1, + 2, + 1, + 3 + ], + [ + 1, + 1, + 2, + 1, + 3 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5, + 2, + 1 + ], + [ + 3, + 4, + 5, + 2, + 1 + ], + [ + 3, + 4, + 5, + 2, + 1 + ], + [ + 3, + 4, + 5, + 2, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3, + 3, + 5, + 4 + ], + [ + 4, + 3, + 3, + 5, + 4 + ], + [ + 4, + 3, + 3, + 5, + 4 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 5, + 4, + 5 + ], + [ + 4, + 5, + 4, + 5 + ], + [ + 4, + 5, + 4, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 5, + 3, + 4 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 3 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 2, + 2, + 4, + 1, + 5 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 1, + 4, + 2 + ], + [ + 1, + 3, + 1, + 4, + 2 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 4, + 1, + 3 + ], + [ + 4, + 2, + 4, + 1, + 3 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 2, + 2, + 4, + 3, + 3 + ], + [ + 2, + 2, + 4, + 3, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 1, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 2, + 5, + 4, + 1 + ], + [ + 3, + 2, + 5, + 4, + 1 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 3 + ], + [ + 1, + 1, + 4, + 1, + 3 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 5, + 3, + 5, + 1, + 4 + ], + [ + 5, + 3, + 5, + 1, + 4 + ], + [ + 5, + 3, + 5, + 1, + 4 + ], + [ + 5, + 3, + 5, + 1, + 4 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 4, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 2, + 3, + 1 + ], + [ + 4, + 4, + 2, + 3, + 1 + ], + [ + 4, + 4, + 2, + 3, + 1 + ], + [ + 4, + 4, + 2, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5, + 4, + 1, + 5 + ], + [ + 3, + 5, + 4, + 1, + 5 + ], + [ + 3, + 5, + 4, + 1, + 5 + ], + [ + 3, + 5, + 4, + 1, + 5 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 5, + 5 + ], + [ + 2, + 1, + 4, + 5, + 5 + ], + [ + 2, + 1, + 4, + 5, + 5 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 2 + ], + [ + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 3, + 2, + 5, + 5 + ], + [ + 2, + 3, + 2, + 5, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 3, + 5, + 1, + 5, + 5 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 3 + ], + [ + 5, + 1, + 3, + 1, + 3 + ], + [ + 5, + 1, + 3, + 1, + 3 + ], + [ + 5, + 1, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 4, + 2, + 3 + ], + [ + 1, + 4, + 4, + 2, + 3 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 3 + ], + [ + 1, + 4, + 4, + 1, + 3 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1, + 2, + 1, + 5, + 3 + ], + [ + 1, + 2, + 1, + 5, + 3 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 4, + 4, + 2, + 2, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 4, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4, + 5, + 5 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 5 + ], + [ + 2, + 4, + 5, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 2, + 4, + 1, + 5 + ], + [ + 4, + 2, + 4, + 1, + 5 + ], + [ + 4, + 2, + 4, + 1, + 4 + ], + [ + 4, + 2, + 4, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 5, + 4 + ], + [ + 2, + 5, + 1, + 5, + 4 + ], + [ + 2, + 5, + 1, + 5, + 4 + ], + [ + 2, + 5, + 1, + 4, + 4 + ], + [ + 2, + 5, + 1, + 4, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5, + 1, + 3, + 1 + ], + [ + 5, + 5, + 1, + 3, + 1 + ], + [ + 5, + 5, + 1, + 3, + 1 + ], + [ + 5, + 5, + 1, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 5, + 4, + 2 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 3, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 5, + 4, + 5 + ], + [ + 3, + 5, + 4, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 2, + 4, + 3, + 2, + 2 + ], + [ + 2, + 4, + 3, + 2, + 2 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 4, + 5, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 5, + 1 + ], + [ + 4, + 1, + 3, + 5, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3, + 1 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4, + 2 + ], + [ + 3, + 4, + 2, + 4, + 2 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 4, + 2, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 4, + 4 + ], + [ + 2, + 5, + 2, + 4, + 1 + ], + [ + 2, + 5, + 4, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 3, + 2 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 3, + 4, + 2, + 3 + ], + [ + 4, + 3, + 4, + 2, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 5, + 3, + 3 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1, + 4, + 5, + 3 + ], + [ + 4, + 1, + 4, + 5, + 3 + ], + [ + 4, + 1, + 4, + 5, + 3 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 2 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 1, + 3, + 2, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 5 + ], + [ + 2, + 5, + 5, + 5 + ], + [ + 2, + 5, + 5, + 5 + ], + [ + 2, + 5, + 5, + 5, + 1 + ], + [ + 2, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3, + 4, + 3 + ], + [ + 3, + 1, + 3, + 4, + 3 + ], + [ + 3, + 1, + 3, + 4, + 3 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 2, + 1, + 4, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 5 + ], + [ + 5, + 3, + 2, + 1, + 5 + ], + [ + 5, + 3, + 2, + 1, + 5 + ], + [ + 5, + 3, + 2, + 1, + 5 + ], + [ + 5, + 3, + 2, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 5 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 4, + 4, + 4, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 5, + 3, + 3 + ], + [ + 3, + 5, + 5, + 3, + 3 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 3, + 2, + 3, + 5, + 5 + ], + [ + 3, + 2, + 3, + 5, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 4, + 1 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 2, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 5, + 3 + ], + [ + 3, + 5, + 5, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 4, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 2, + 3, + 2, + 3 + ], + [ + 3, + 2, + 3, + 2, + 3 + ], + [ + 3, + 2, + 3, + 2, + 3 + ], + [ + 3, + 2, + 3, + 2, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 2, + 2, + 5, + 5, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 3 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 5, + 5 + ], + [ + 3, + 3, + 5, + 5, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 5, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 2, + 1, + 3, + 2, + 4 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 1, + 3, + 4, + 4 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 3, + 2, + 4, + 4, + 3 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 3, + 3, + 1 + ], + [ + 3, + 2, + 3, + 3, + 1 + ], + [ + 3, + 2, + 3, + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 3, + 3, + 4, + 4 + ], + [ + 3, + 3, + 4, + 4 + ], + [ + 3, + 3, + 4, + 4 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 2, + 4, + 4, + 2, + 2 + ], + [ + 2, + 4, + 4, + 2, + 2 + ], + [ + 2, + 4, + 4, + 2, + 2 + ], + [ + 2, + 4, + 4, + 2, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 2, + 5 + ], + [ + 4, + 1, + 2, + 5 + ], + [ + 4, + 1, + 2, + 5 + ], + [ + 4, + 1, + 2, + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 5 + ], + [ + 4, + 1, + 1, + 2, + 5 + ], + [ + 4, + 2, + 1, + 2, + 5 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3, + 5, + 5 + ], + [ + 4, + 2, + 3, + 5, + 5 + ], + [ + 4, + 2, + 3, + 5, + 5 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 5, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 3, + 4 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 3, + 4, + 4 + ], + [ + 5, + 3, + 3, + 4, + 4 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 5, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 4, + 2, + 1, + 2, + 3 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 2, + 5, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2, + 2, + 3 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 2, + 3, + 4 + ], + [ + 2, + 4, + 2, + 3, + 4 + ], + [ + 2, + 4, + 2, + 3, + 4 + ], + [ + 2, + 4, + 2, + 3, + 4 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 2 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 2, + 2, + 3 + ], + [ + 2, + 2, + 3, + 5, + 4 + ], + [ + 2, + 2, + 3, + 5, + 1 + ], + [ + 2, + 2, + 3, + 5, + 1 + ], + [ + 2, + 2, + 3, + 5, + 4 + ], + [ + 2, + 5, + 3, + 5, + 4 + ], + [ + 2, + 5, + 3, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 2, + 5, + 4, + 2, + 3 + ], + [ + 2, + 5, + 4, + 2, + 3 + ], + [ + 2, + 5, + 4, + 2, + 3 + ], + [ + 2, + 5, + 4, + 2, + 3 + ], + [ + 2, + 5, + 4, + 2, + 1 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 2, + 4, + 2, + 2 + ], + [ + 2, + 4, + 2, + 2 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 1, + 5 + ], + [ + 3, + 2, + 4, + 1, + 5 + ], + [ + 3, + 2, + 4, + 1, + 5 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 2 + ], + [ + 3, + 1, + 2, + 1, + 2 + ], + [ + 3, + 1, + 2, + 1, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 5, + 1, + 2, + 2 + ], + [ + 3, + 5, + 1, + 2, + 2 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 3 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 5, + 4 + ], + [ + 3, + 3, + 5, + 5, + 4 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 5, + 4, + 2 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 4, + 5, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 3, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 1, + 4, + 1 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 5, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 3, + 1 + ], + [ + 3, + 5, + 1, + 3, + 1 + ], + [ + 3, + 5, + 1, + 3, + 1 + ], + [ + 3, + 5, + 1, + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 3, + 1, + 2, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4, + 2, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 3, + 4, + 3 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 5, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 2, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 5 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 2, + 4, + 4 + ], + [ + 1, + 2, + 4, + 4 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 2, + 1, + 3 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 2, + 4 + ], + [ + 2, + 1, + 2, + 2, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 4, + 2, + 4, + 3, + 3 + ], + [ + 4, + 2, + 4, + 3, + 3 + ], + [ + 4, + 2, + 4, + 3, + 3 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 1, + 2 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 2, + 1, + 4 + ], + [ + 1, + 2, + 2, + 1, + 4 + ], + [ + 1, + 2, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 3, + 3 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3, + 5 + ], + [ + 3, + 4, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 2, + 5, + 1 + ], + [ + 4, + 4, + 2, + 5, + 1 + ], + [ + 4, + 4, + 2, + 5, + 1 + ], + [ + 4, + 4, + 2, + 5, + 1 + ], + [ + 4, + 4, + 2, + 5, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 5, + 3 + ], + [ + 4, + 1, + 4, + 3, + 3 + ], + [ + 4, + 1, + 4, + 3, + 3 + ], + [ + 4, + 1, + 4, + 3, + 3 + ], + [ + 4, + 1, + 4, + 3, + 3 + ], + [ + 4, + 1, + 1, + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 5, + 2 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 3, + 4, + 2, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 4, + 1, + 4, + 5, + 1 + ], + [ + 4, + 1, + 2, + 1, + 4 + ], + [ + 4, + 1, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 5, + 5, + 5 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 1, + 4, + 3 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 2, + 3, + 2, + 5, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 1, + 5, + 3, + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 4, + 4, + 5, + 2, + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 3 + ], + [ + 4, + 5, + 2, + 3 + ], + [ + 4, + 5, + 2, + 3 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 2 + ], + [ + 2, + 2, + 5, + 2, + 2 + ], + [ + 2, + 2, + 5, + 2, + 2 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 5, + 2, + 2 + ], + [ + 1, + 2, + 5, + 2, + 2 + ], + [ + 1, + 2, + 5, + 2, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 4 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 4, + 1, + 2 + ], + [ + 5, + 3, + 4, + 1, + 2 + ], + [ + 5, + 3, + 4, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5, + 4, + 1 + ], + [ + 1, + 5, + 5, + 4, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 5, + 2, + 3, + 3 + ], + [ + 4, + 5, + 2, + 3, + 3 + ], + [ + 4, + 5, + 2, + 3, + 3 + ], + [ + 4, + 5, + 2, + 3, + 3 + ], + [ + 4, + 5, + 2, + 3, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 4, + 1 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 1, + 5, + 2 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 5, + 1, + 3, + 4, + 3 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 5, + 3, + 4, + 1, + 2 + ], + [ + 5, + 3, + 4, + 1, + 2 + ], + [ + 5, + 3, + 4, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 3, + 3, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 2, + 4, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 2 + ], + [ + 1, + 4, + 4, + 4, + 1 + ], + [ + 1, + 1, + 4, + 4, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 2, + 3, + 4, + 4 + ], + [ + 5, + 2, + 3, + 4, + 4 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 1, + 3, + 2, + 5, + 4 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 4 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 5, + 4, + 2, + 2, + 1 + ], + [ + 5, + 4, + 2, + 2, + 1 + ], + [ + 5, + 4, + 2, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 5, + 4, + 5 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 5, + 5, + 4, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 4, + 2, + 2 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 2, + 4, + 3 + ], + [ + 2, + 3, + 2, + 4, + 3 + ], + [ + 2, + 3, + 2, + 4, + 3 + ], + [ + 2, + 3, + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 2, + 4, + 3 + ], + [ + 5, + 1, + 2, + 4, + 3 + ], + [ + 5, + 1, + 2, + 4, + 3 + ], + [ + 5, + 1, + 2, + 4, + 3 + ], + [ + 5, + 1, + 2, + 4, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 3, + 2, + 5, + 1 + ], + [ + 5, + 3, + 2, + 5, + 1 + ], + [ + 5, + 3, + 2, + 5, + 1 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 3, + 2 + ], + [ + 2, + 4, + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 5, + 2, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2, + 2 + ], + [ + 3, + 2, + 1, + 5, + 3 + ], + [ + 3, + 2, + 1, + 5, + 3 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 5, + 4 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 5, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 4, + 3, + 2, + 5 + ], + [ + 4, + 4, + 3, + 2, + 5 + ], + [ + 4, + 4, + 3, + 2, + 5 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 2, + 5, + 5, + 2, + 3 + ], + [ + 2, + 1, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 5, + 1, + 4, + 5 + ], + [ + 1, + 5, + 1, + 4, + 5 + ], + [ + 1, + 5, + 3, + 1, + 1 + ], + [ + 1, + 5, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 5, + 1, + 4, + 2 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 1, + 4, + 3, + 5 + ], + [ + 5, + 1, + 4, + 3, + 5 + ], + [ + 5, + 1, + 4, + 3, + 5 + ], + [ + 5, + 1, + 4, + 3, + 5 + ], + [ + 5, + 1, + 4, + 3, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 5, + 3, + 5, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 2, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 2, + 2, + 2, + 3 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 2 + ], + [ + 3, + 4, + 3, + 1, + 2 + ], + [ + 3, + 4, + 3, + 1, + 2 + ], + [ + 3, + 4, + 3, + 1, + 2 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 2, + 4 + ], + [ + 1, + 2, + 1, + 2, + 4 + ], + [ + 1, + 2, + 1, + 2, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5, + 2, + 5, + 4 + ], + [ + 3, + 5, + 2, + 5, + 4 + ], + [ + 3, + 5, + 2, + 5, + 4 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 3, + 5, + 2, + 5 + ], + [ + 3, + 3, + 5, + 2, + 5 + ], + [ + 3, + 3, + 5, + 2, + 5 + ], + [ + 3, + 3, + 5, + 2, + 5 + ], + [ + 3, + 3, + 5, + 2, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 3, + 1, + 4, + 2, + 5 + ], + [ + 3, + 1, + 4, + 2, + 5 + ], + [ + 3, + 1, + 4, + 1, + 2 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 3 + ], + [ + 3, + 2, + 5, + 1, + 3 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 5, + 3, + 2, + 4, + 2 + ], + [ + 5, + 3, + 2, + 4, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4, + 5, + 1, + 3 + ], + [ + 2, + 4, + 5, + 1, + 3 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 2 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4, + 4, + 4 + ], + [ + 1, + 1, + 4, + 4, + 1 + ], + [ + 1, + 1, + 4, + 4, + 1 + ], + [ + 1, + 1, + 4, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 4, + 3, + 2, + 5 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 2 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 3, + 1, + 4, + 2, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 3, + 4, + 3, + 1 + ], + [ + 2, + 3, + 4, + 3, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 4 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 2, + 5, + 1 + ], + [ + 5, + 4, + 2, + 5, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 3, + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 4, + 5 + ], + [ + 4, + 5, + 2, + 4, + 5 + ], + [ + 4, + 5, + 2, + 4, + 5 + ], + [ + 4, + 5, + 2, + 4, + 5 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 4, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 4, + 2, + 4 + ], + [ + 3, + 2, + 4, + 4, + 2 + ], + [ + 3, + 2, + 4, + 4, + 2 + ], + [ + 3, + 2, + 4, + 4, + 2 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 3, + 4, + 1 + ], + [ + 2, + 3, + 3, + 4, + 1 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3, + 3 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 1, + 2, + 3 + ], + [ + 2, + 5, + 1, + 5, + 4 + ], + [ + 2, + 4, + 3, + 1, + 5 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 4, + 5 + ], + [ + 5, + 1, + 4, + 4, + 5 + ], + [ + 5, + 1, + 4, + 4, + 5 + ], + [ + 5, + 1, + 4, + 1, + 5 + ], + [ + 5, + 1, + 4, + 1, + 5 + ], + [ + 5, + 1, + 4, + 1, + 5 + ], + [ + 5, + 1, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 2, + 2, + 4 + ], + [ + 1, + 3, + 2, + 2, + 4 + ], + [ + 1, + 3, + 2, + 2, + 4 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 5, + 5, + 4 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 1, + 3, + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + 5 + ], + [ + 4, + 5, + 3, + 1, + 4 + ], + [ + 4, + 5, + 3, + 1, + 4 + ], + [ + 4, + 5, + 3, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 5, + 4, + 5 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 5, + 4, + 1 + ], + [ + 5, + 4, + 5, + 4, + 1 + ], + [ + 5, + 4, + 5, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 3, + 4 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 1, + 1, + 3, + 3 + ], + [ + 4, + 1, + 1, + 3, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 1, + 4, + 4, + 3, + 5 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 5, + 4 + ], + [ + 4, + 5, + 1, + 5, + 4 + ], + [ + 4, + 5, + 1, + 5, + 4 + ], + [ + 4, + 5, + 1, + 5, + 4 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 2, + 3, + 3, + 5, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 2 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 1, + 4, + 5, + 5 + ], + [ + 5, + 2, + 4, + 5, + 3 + ], + [ + 5, + 2, + 4, + 5, + 3 + ], + [ + 5, + 2, + 4, + 5, + 3 + ], + [ + 5, + 2, + 4, + 5, + 3 + ], + [ + 5, + 2, + 4, + 5, + 3 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1, + 2 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 2, + 2, + 2 + ], + [ + 3, + 1, + 2, + 2, + 2 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 3, + 2 + ], + [ + 1, + 1, + 2, + 3, + 2 + ], + [ + 1, + 1, + 2, + 3, + 2 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 2, + 3, + 3, + 1, + 4 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 1, + 2, + 5, + 2, + 1 + ], + [ + 1, + 2, + 5, + 2, + 1 + ], + [ + 1, + 2, + 5, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 5 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 5, + 1 + ], + [ + 3, + 5, + 1, + 5, + 1 + ], + [ + 3, + 5, + 1, + 5, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 2, + 3 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 3 + ], + [ + 3, + 4, + 3, + 1, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 5, + 2, + 5, + 2, + 3 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 5 + ], + [ + 4, + 1, + 1, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 3, + 1, + 2, + 5, + 5 + ], + [ + 3, + 1, + 2, + 5, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 1, + 2, + 4 + ], + [ + 3, + 3, + 1, + 2, + 4 + ], + [ + 3, + 3, + 1, + 2, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 4, + 1, + 1, + 5 + ], + [ + 1, + 4, + 1, + 1, + 5 + ], + [ + 1, + 4, + 1, + 1, + 5 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 4, + 2, + 2 + ], + [ + 3, + 3, + 4, + 2, + 2 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 5, + 2 + ], + [ + 4, + 1, + 5, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 3, + 3, + 4, + 4 + ], + [ + 5, + 3, + 3, + 4, + 4 + ], + [ + 5, + 3, + 3, + 4, + 1 + ], + [ + 5, + 3, + 3, + 4, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 2, + 4, + 4 + ], + [ + 1, + 1, + 2, + 4, + 4 + ], + [ + 1, + 1, + 2, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 4 + ], + [ + 4, + 1, + 3, + 1, + 4 + ], + [ + 4, + 1, + 3, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 2, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 2, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 3, + 3, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 2, + 4, + 2 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 5 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 5, + 2, + 2, + 4 + ], + [ + 1, + 5, + 3, + 1, + 5 + ], + [ + 1, + 5, + 1, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 4, + 3 + ], + [ + 5, + 3, + 1, + 4, + 3 + ], + [ + 5, + 3, + 1, + 4, + 3 + ], + [ + 5, + 3, + 1, + 5, + 1 + ], + [ + 5, + 3, + 1, + 5, + 1 + ], + [ + 5, + 3, + 1, + 5, + 1 + ], + [ + 5, + 3, + 1, + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 4, + 3, + 4 + ], + [ + 4, + 2, + 5, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 4, + 5, + 5 + ], + [ + 2, + 3, + 4, + 5, + 5 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 1, + 1, + 3, + 3, + 2 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 4 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 5, + 4, + 4, + 2 + ], + [ + 5, + 5, + 4, + 4, + 2 + ], + [ + 5, + 5, + 4, + 4, + 2 + ], + [ + 5, + 1, + 2, + 2, + 2 + ], + [ + 5, + 1, + 2, + 2, + 2 + ], + [ + 5, + 1, + 2, + 2, + 2 + ], + [ + 5, + 1, + 2, + 2, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 4, + 4, + 1 + ], + [ + 2, + 3, + 4, + 4, + 1 + ], + [ + 2, + 3, + 4, + 4, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2, + 1, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 5, + 1, + 3 + ], + [ + 3, + 3, + 5, + 1, + 3 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 2, + 5 + ], + [ + 1, + 5, + 2, + 5 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 2, + 5 + ], + [ + 4, + 1, + 5, + 2, + 5 + ], + [ + 4, + 1, + 5, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 2 + ], + [ + 4, + 3, + 3, + 2, + 2 + ], + [ + 4, + 3, + 3, + 2, + 2 + ], + [ + 4, + 3, + 3, + 2, + 2 + ], + [ + 4, + 3, + 3, + 2, + 2 + ], + [ + 4, + 1, + 3, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 5, + 2, + 5 + ], + [ + 1, + 5, + 2, + 5 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 4, + 4, + 5, + 3 + ], + [ + 3, + 4, + 4, + 5, + 3 + ], + [ + 3, + 4, + 4, + 5, + 3 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 5, + 3, + 2, + 3, + 5 + ], + [ + 5, + 3, + 2, + 3, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 1, + 1, + 3, + 2, + 2 + ], + [ + 1, + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 3, + 2 + ], + [ + 1 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 5, + 5, + 1 + ], + [ + 3, + 4, + 5, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 4, + 3 + ], + [ + 1, + 5, + 4, + 3 + ], + [ + 1, + 5, + 4, + 3 + ], + [ + 2, + 4, + 5, + 5 + ], + [ + 2, + 4, + 5, + 5 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2, + 1 + ], + [ + 1, + 5, + 3, + 1, + 1 + ], + [ + 1, + 5, + 3, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 2, + 3, + 5 + ], + [ + 5, + 1, + 2, + 3, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 4, + 1, + 4, + 5, + 2 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 3, + 5, + 5, + 2, + 1 + ], + [ + 3, + 5, + 5, + 2, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 5 + ], + [ + 3, + 5, + 5, + 1, + 5 + ], + [ + 3, + 5, + 5, + 1, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 4, + 1, + 4, + 1 + ], + [ + 1, + 4, + 4, + 4, + 1 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 4, + 4 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 4, + 2, + 2, + 4 + ], + [ + 4, + 4, + 2, + 2, + 4 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 2, + 2, + 1 + ], + [ + 1, + 5, + 2, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 3 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 5, + 4, + 1 + ], + [ + 4, + 2, + 5, + 4, + 1 + ], + [ + 4, + 2, + 5, + 4, + 1 + ], + [ + 4, + 2, + 5, + 4, + 1 + ], + [ + 3, + 5, + 5, + 4 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 1, + 3, + 5, + 3, + 3 + ], + [ + 1, + 3, + 5, + 3, + 1 + ], + [ + 1, + 3, + 5, + 3, + 1 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 1, + 3, + 5, + 1, + 1 + ], + [ + 4, + 1, + 3, + 3, + 3 + ], + [ + 3, + 2, + 2, + 3, + 1 + ], + [ + 3, + 1, + 2, + 2, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2, + 5, + 5, + 3 + ], + [ + 5, + 2, + 5, + 5, + 3 + ], + [ + 5, + 2, + 5, + 5, + 3 + ], + [ + 5, + 2, + 5, + 5, + 3 + ], + [ + 5, + 2, + 5, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 5, + 4 + ], + [ + 4, + 3, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1, + 3, + 3, + 4, + 4 + ], + [ + 1, + 3, + 3, + 4, + 4 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 4, + 2, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 2, + 1 + ], + [ + 1, + 3, + 1, + 2, + 1 + ], + [ + 1, + 3, + 1, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1, + 2 + ], + [ + 3, + 2, + 5, + 3, + 4 + ], + [ + 3, + 2, + 5, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5, + 3, + 2, + 2 + ], + [ + 5, + 3, + 2, + 2 + ], + [ + 5, + 3, + 2, + 2 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 2, + 4, + 3 + ], + [ + 5, + 5, + 2, + 4, + 3 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 5, + 5, + 2, + 1, + 4 + ], + [ + 1, + 4, + 5, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 4, + 2, + 2 + ], + [ + 5, + 3, + 4, + 2, + 2 + ], + [ + 5, + 3, + 4, + 2, + 2 + ], + [ + 5, + 5, + 4, + 2, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + 4, + 3, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 3 + ], + [ + 4, + 2, + 1, + 1, + 3 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 5, + 4, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 2, + 4, + 2 + ], + [ + 1, + 4, + 2, + 4, + 2 + ], + [ + 1, + 4, + 2, + 4, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 2, + 5, + 3, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 4, + 2 + ], + [ + 4, + 2, + 3, + 1, + 2 + ], + [ + 4, + 2, + 3, + 1, + 2 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 5, + 3, + 3 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 4, + 5, + 4, + 1 + ], + [ + 5, + 4, + 5, + 4, + 1 + ], + [ + 5, + 4, + 5, + 4, + 4 + ], + [ + 5, + 4, + 5, + 4, + 4 + ], + [ + 5, + 4, + 5, + 4, + 4 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 3, + 1, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 3, + 4, + 5, + 5, + 5 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 3, + 4 + ], + [ + 1, + 3, + 1, + 3, + 4 + ], + [ + 1, + 3, + 1, + 3, + 4 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 1, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 5, + 2, + 2, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 3, + 5, + 1 + ], + [ + 1, + 5, + 3, + 5, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4, + 3, + 3 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 2, + 4 + ], + [ + 1, + 3, + 2, + 4 + ], + [ + 1, + 3, + 2, + 4 + ], + [ + 1, + 3, + 2, + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 2, + 3, + 3 + ], + [ + 4, + 1, + 2, + 3, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 2, + 5, + 1 + ], + [ + 2, + 2, + 5, + 1, + 5 + ], + [ + 2, + 1, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 4, + 5, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 1, + 1, + 3, + 4, + 2 + ], + [ + 1, + 1, + 3, + 4, + 2 + ], + [ + 1, + 1, + 3, + 4, + 2 + ], + [ + 1, + 1, + 3, + 4, + 2 + ], + [ + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 3, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 5 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 4, + 1, + 4, + 1, + 4 + ], + [ + 4, + 1, + 4, + 1, + 4 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 4, + 2 + ], + [ + 3, + 3, + 5, + 4, + 2 + ], + [ + 3, + 3, + 5, + 4, + 2 + ], + [ + 3, + 3, + 5, + 4, + 2 + ], + [ + 3, + 3, + 5, + 4, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 4, + 1 + ], + [ + 5, + 5, + 2, + 4, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 4, + 3, + 4, + 4 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 4, + 5 + ], + [ + 4, + 2, + 4, + 5 + ], + [ + 4, + 2, + 4, + 5 + ], + [ + 4, + 2, + 4, + 5 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 3, + 3, + 3 + ], + [ + 2, + 4, + 5, + 1, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 5, + 4, + 5 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 5, + 4, + 5 + ], + [ + 2, + 4, + 5, + 4, + 5 + ], + [ + 2, + 4, + 5, + 4, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 3, + 2, + 4 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 4, + 4 + ], + [ + 4, + 1, + 5, + 4, + 4 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 1, + 3, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 3, + 4, + 5, + 2, + 4 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 3, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3, + 2 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 3, + 4, + 4, + 3, + 4 + ], + [ + 3, + 4, + 4, + 3, + 4 + ], + [ + 3, + 4, + 4, + 3, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 1, + 4, + 5 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 2, + 2, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 3, + 5, + 5 + ], + [ + 5, + 1, + 3, + 5, + 5 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 2, + 5, + 3 + ], + [ + 5, + 5, + 2, + 5, + 3 + ], + [ + 1, + 2, + 4, + 5, + 2 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 4, + 5 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 2, + 4, + 4, + 4 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 2, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 5, + 1, + 4, + 4 + ], + [ + 1, + 5, + 1, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 3, + 5, + 3, + 1 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 1, + 3, + 5, + 2, + 3 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 3, + 3, + 2 + ], + [ + 5, + 2, + 3, + 3, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 1, + 1, + 2, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 4, + 3, + 4 + ], + [ + 1, + 2, + 4, + 3, + 4 + ], + [ + 1, + 2, + 4, + 4, + 1 + ], + [ + 1, + 2, + 4, + 4, + 1 + ], + [ + 1, + 2, + 4, + 4, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 5, + 2 + ], + [ + 1, + 2, + 3, + 5, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 5, + 1 + ], + [ + 1, + 2, + 3, + 5, + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 4, + 5 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 1, + 5, + 3 + ], + [ + 2, + 5, + 1, + 5, + 3 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 4, + 1 + ], + [ + 2, + 3, + 5, + 4, + 1 + ], + [ + 2, + 3, + 5, + 4, + 1 + ], + [ + 2, + 3, + 5, + 4, + 1 + ], + [ + 2, + 3, + 5, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3, + 2, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 4, + 5 + ], + [ + 3, + 1, + 4, + 4, + 1 + ], + [ + 3, + 1, + 4, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 1, + 4, + 4, + 5, + 5 + ], + [ + 1, + 4, + 4, + 5, + 5 + ], + [ + 1, + 4, + 4, + 2, + 1 + ], + [ + 1, + 4, + 3, + 1, + 2 + ], + [ + 1, + 4, + 3, + 1, + 2 + ], + [ + 1, + 2, + 3, + 1, + 2 + ], + [ + 1, + 2, + 3, + 1, + 2 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 3, + 1, + 3, + 3, + 1 + ], + [ + 3, + 1, + 3, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 2, + 3, + 1 + ], + [ + 3, + 1, + 2, + 3, + 1 + ], + [ + 3, + 1, + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 2 + ], + [ + 2, + 4, + 5, + 2 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 5, + 4, + 2, + 2, + 3 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 3, + 5 + ], + [ + 4, + 3, + 1, + 3, + 5 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 3 + ], + [ + 1, + 1, + 4, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 5, + 5, + 4 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 3, + 1 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2, + 4, + 2, + 3 + ], + [ + 1, + 2, + 4, + 2, + 3 + ], + [ + 1, + 2, + 4, + 2, + 3 + ], + [ + 1, + 2, + 4, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 3, + 1 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 3, + 1, + 3 + ], + [ + 4, + 5, + 3, + 1, + 3 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 4 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 2, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 2, + 4 + ], + [ + 1, + 1, + 4, + 2, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 5, + 5, + 2 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 2, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 5, + 2, + 1, + 1 + ], + [ + 2, + 4, + 3, + 4, + 2 + ], + [ + 2, + 4, + 3, + 4, + 2 + ], + [ + 2, + 4, + 3, + 2, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 3, + 2, + 4 + ], + [ + 5, + 5, + 3, + 2, + 4 + ], + [ + 5, + 1, + 1, + 5, + 3 + ], + [ + 5, + 1, + 1, + 5, + 3 + ], + [ + 5, + 1, + 1, + 5, + 3 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 2, + 3, + 2 + ], + [ + 2, + 3, + 2, + 3, + 2 + ], + [ + 2, + 3, + 2, + 3, + 2 + ], + [ + 2, + 3, + 2, + 3, + 2 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 1, + 5 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 2, + 2, + 5, + 5, + 3 + ], + [ + 2, + 2, + 5, + 5, + 3 + ], + [ + 2, + 2, + 5, + 5, + 3 + ], + [ + 2, + 2, + 1, + 5, + 3 + ], + [ + 2, + 2, + 1, + 5, + 3 + ], + [ + 2, + 2, + 1, + 5, + 3 + ], + [ + 2, + 2, + 1, + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 4, + 3, + 3 + ], + [ + 3, + 1, + 4, + 3, + 3 + ], + [ + 3, + 1, + 1, + 4, + 3 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 2, + 2, + 3, + 2, + 5 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 2 + ], + [ + 1, + 5, + 2, + 4, + 5 + ], + [ + 1, + 5, + 2, + 4, + 5 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 4, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 3, + 1 + ], + [ + 1, + 5, + 1, + 3, + 1 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 3, + 5, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 1, + 3, + 3 + ], + [ + 4, + 4, + 1, + 3, + 3 + ], + [ + 4, + 4, + 1, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 3, + 5 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5, + 3, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 4, + 2, + 4, + 4 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 3, + 1 + ], + [ + 5, + 1, + 5, + 3, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 4, + 4, + 2, + 5, + 5 + ], + [ + 4, + 4, + 2, + 5, + 5 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 5 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 2, + 5 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 4, + 3, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 5, + 4, + 3 + ], + [ + 1, + 2, + 5, + 4, + 3 + ], + [ + 1, + 2, + 5, + 4, + 3 + ], + [ + 1, + 2, + 5, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 4, + 2, + 2, + 1 + ], + [ + 3, + 4, + 2, + 2, + 1 + ], + [ + 3, + 4, + 2, + 2, + 1 + ], + [ + 3, + 4, + 2, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 2, + 1, + 5, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 4, + 1, + 5, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2, + 2 + ], + [ + 5, + 5, + 2, + 2, + 2 + ], + [ + 5, + 5, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 5, + 3, + 3 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 1, + 2, + 4, + 4, + 1 + ], + [ + 1, + 1, + 2, + 4, + 4 + ], + [ + 1, + 1, + 2, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 1, + 2, + 3, + 5, + 4 + ], + [ + 1, + 2, + 3, + 5, + 4 + ], + [ + 1, + 2, + 3, + 5, + 4 + ], + [ + 1, + 2, + 3, + 1, + 5 + ], + [ + 1, + 2, + 3, + 1, + 5 + ], + [ + 1, + 2, + 3, + 1, + 5 + ], + [ + 1, + 2, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 4, + 2 + ], + [ + 2, + 1, + 4, + 4, + 2 + ], + [ + 2, + 1, + 4, + 4, + 4 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 1, + 2, + 4, + 3, + 3 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 3, + 2, + 3 + ], + [ + 1, + 3, + 3, + 2, + 3 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 5, + 5, + 4 + ], + [ + 5, + 4, + 1, + 5, + 5 + ], + [ + 5, + 4, + 1, + 5, + 5 + ], + [ + 5, + 4, + 1, + 5, + 5 + ], + [ + 5, + 4, + 1, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4, + 5 + ], + [ + 4, + 2, + 1, + 4, + 5 + ], + [ + 4, + 2, + 1, + 4, + 5 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 3, + 2, + 3, + 5, + 2 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 3, + 2, + 1, + 1, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2, + 3 + ], + [ + 1, + 1, + 2, + 2, + 3 + ], + [ + 1, + 1, + 2, + 2, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 2, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 4, + 3, + 3 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 2, + 4, + 3 + ], + [ + 3, + 2, + 2, + 4, + 3 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 5, + 4, + 4, + 2 + ], + [ + 3, + 5, + 4, + 4, + 2 + ], + [ + 3, + 5, + 4, + 4, + 2 + ], + [ + 3, + 5, + 4, + 4, + 2 + ], + [ + 3, + 5, + 4, + 3, + 1 + ], + [ + 3, + 5, + 4, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 5, + 4 + ], + [ + 5, + 4, + 3, + 5, + 4 + ], + [ + 5, + 4, + 3, + 5, + 4 + ], + [ + 5, + 4, + 3, + 5, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 4, + 5, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 3, + 4, + 5, + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 4, + 4, + 5 + ], + [ + 5, + 4, + 4, + 4, + 5 + ], + [ + 5, + 4, + 4, + 4, + 5 + ], + [ + 5, + 4, + 4, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 4 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1, + 2 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 1, + 5, + 5, + 1, + 4 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 4, + 1 + ], + [ + 1, + 5, + 1, + 2, + 5 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 5, + 3 + ], + [ + 4, + 2, + 1, + 5, + 3 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 5, + 3, + 4 + ], + [ + 3, + 1, + 5, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 3, + 3, + 1, + 4 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 4, + 5, + 3, + 2, + 1 + ], + [ + 4, + 5, + 3, + 2, + 1 + ], + [ + 4, + 5, + 3, + 2, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 1, + 5, + 2, + 4, + 1 + ], + [ + 1, + 5, + 2, + 4, + 1 + ], + [ + 1, + 5, + 2, + 4, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 3, + 3 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3, + 1, + 3, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 3, + 5, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 4, + 2, + 4, + 3 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 2, + 4, + 5, + 3 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 3 + ], + [ + 5, + 2, + 2, + 5, + 1 + ], + [ + 5, + 2, + 2, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 2 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 4, + 3, + 2 + ], + [ + 1, + 5, + 4, + 3, + 1 + ], + [ + 1, + 5, + 4, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 4, + 5, + 2 + ], + [ + 3, + 4, + 5, + 2 + ], + [ + 3, + 4, + 5, + 2 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 5, + 1 + ], + [ + 2, + 5, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 5, + 4, + 5, + 2 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 3, + 4 + ], + [ + 3, + 4, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 5, + 5, + 3, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 1, + 3 + ], + [ + 4, + 3, + 1, + 3 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 3, + 4, + 5, + 4 + ], + [ + 2, + 3, + 4, + 5, + 4 + ], + [ + 2, + 3, + 4, + 5, + 4 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 3, + 1, + 3, + 2 + ], + [ + 1, + 3, + 1, + 3, + 2 + ], + [ + 1, + 3, + 1, + 3, + 2 + ], + [ + 5, + 2, + 3, + 4, + 5 + ], + [ + 5, + 2, + 3, + 4, + 5 + ], + [ + 5, + 2, + 3, + 4, + 1 + ], + [ + 5, + 2, + 3, + 4, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 2, + 3, + 4, + 4 + ], + [ + 1, + 2, + 3, + 4, + 4 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5, + 3, + 5, + 2 + ], + [ + 5, + 3, + 5, + 2 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 3, + 3, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 3, + 1, + 4, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 3, + 3, + 4, + 5 + ], + [ + 4, + 1, + 1, + 3, + 2 + ], + [ + 4, + 1, + 1, + 3, + 2 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 2, + 1, + 3, + 3, + 1 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3, + 2, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 1, + 1, + 2 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 2, + 4, + 2, + 5 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 1, + 3, + 5 + ], + [ + 4, + 2, + 1, + 3, + 5 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 1, + 4 + ], + [ + 3, + 3, + 1, + 4 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 5 + ], + [ + 3, + 4, + 2, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4, + 5 + ], + [ + 3, + 3, + 4, + 5 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2, + 2, + 3, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 3, + 4 + ], + [ + 4, + 5, + 3, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 4, + 2, + 2 + ], + [ + 2, + 1, + 4, + 2, + 2 + ], + [ + 2, + 1, + 4, + 2, + 2 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 3, + 5, + 2, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 4, + 5 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 2 + ], + [ + 3, + 1, + 3, + 2 + ], + [ + 3, + 1, + 3, + 2 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 2, + 1, + 3 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 1, + 2, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 2, + 2, + 3, + 4, + 2 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 4, + 1 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2, + 5 + ], + [ + 4, + 1, + 1, + 2, + 5 + ], + [ + 4, + 5, + 1, + 2, + 5 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1, + 4, + 3 + ], + [ + 4, + 4, + 1, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 5, + 4, + 3, + 5 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 4, + 3, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 3, + 5, + 1, + 5, + 4 + ], + [ + 3, + 5, + 1, + 5, + 4 + ], + [ + 3, + 5, + 1, + 4, + 1 + ], + [ + 3, + 5, + 1, + 4, + 1 + ], + [ + 3, + 5, + 1, + 4, + 1 + ], + [ + 3, + 5, + 1, + 4, + 1 + ], + [ + 3, + 5, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 4, + 4 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 3, + 3, + 4, + 5 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 1, + 5, + 5 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 3 + ], + [ + 5, + 5, + 1, + 5, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 5, + 2, + 2 + ], + [ + 2, + 5, + 5, + 2, + 2 + ], + [ + 2, + 5, + 5, + 2, + 2 + ], + [ + 2, + 5, + 5, + 2, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1, + 4, + 2, + 4 + ], + [ + 1, + 1, + 4, + 2, + 4 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 4, + 1 + ], + [ + 5, + 2, + 5, + 4, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 3, + 2, + 4, + 3, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 1, + 5, + 4 + ], + [ + 2, + 2, + 1, + 5, + 4 + ], + [ + 2, + 2, + 2, + 1, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 3, + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 2, + 1, + 3, + 3 + ], + [ + 5, + 2, + 1, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 5, + 5, + 2 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 4, + 3, + 2, + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 5, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 3, + 2 + ], + [ + 2, + 3, + 1, + 3, + 2 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 5, + 4, + 3, + 5, + 2 + ], + [ + 5, + 4, + 3, + 5, + 1 + ], + [ + 5, + 4, + 3, + 5, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 2, + 5, + 3, + 5 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4, + 1, + 2, + 4 + ], + [ + 5, + 4, + 1, + 2, + 4 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 4, + 2, + 5 + ], + [ + 1, + 2, + 4, + 1, + 2 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 5 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 4, + 5 + ], + [ + 2, + 4, + 4, + 5 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 2, + 1, + 2 + ], + [ + 5, + 1, + 2, + 1, + 2 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 4, + 4 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 3, + 4, + 5 + ], + [ + 4, + 5, + 1, + 1, + 2 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 3, + 4, + 3 + ], + [ + 2, + 4, + 3, + 4, + 1 + ], + [ + 2, + 5, + 4, + 4, + 1 + ], + [ + 2, + 5, + 4, + 4, + 1 + ], + [ + 2, + 5, + 4, + 4, + 1 + ], + [ + 2, + 5, + 4, + 4, + 1 + ], + [ + 2, + 5, + 4, + 4, + 1 + ], + [ + 3, + 5, + 3, + 2, + 4 + ], + [ + 3, + 5, + 3, + 2, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 4, + 4, + 4, + 3 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 5, + 2, + 3, + 4 + ], + [ + 4, + 5, + 2, + 3, + 4 + ], + [ + 4, + 5, + 2, + 3, + 1 + ], + [ + 4, + 5, + 2, + 3, + 1 + ], + [ + 4, + 5, + 2, + 3, + 1 + ], + [ + 4, + 5, + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 1, + 5, + 1, + 1, + 2 + ], + [ + 4, + 5, + 1, + 1, + 3 + ], + [ + 4, + 5, + 1, + 1, + 4 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 4, + 3 + ], + [ + 5, + 3, + 5, + 4, + 3 + ], + [ + 5, + 3, + 5, + 4, + 3 + ], + [ + 5, + 3, + 5, + 4, + 3 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 5, + 4, + 5, + 2, + 3 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 2, + 4, + 3 + ], + [ + 1, + 2, + 2, + 4, + 3 + ], + [ + 1, + 2, + 2, + 4, + 3 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2, + 4, + 3 + ], + [ + 4, + 2, + 2, + 4, + 3 + ], + [ + 4, + 2, + 2, + 4, + 3 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 2, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 2, + 5, + 1, + 4, + 3 + ], + [ + 2, + 5, + 3, + 5, + 2 + ], + [ + 2, + 5, + 3, + 5, + 2 + ], + [ + 2, + 5, + 3, + 5, + 2 + ], + [ + 2, + 5, + 3, + 5, + 2 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 4, + 2, + 5, + 3, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 2, + 3, + 4, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 3, + 3 + ], + [ + 2, + 4, + 1, + 3, + 1 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 4, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 3, + 2, + 4 + ], + [ + 2, + 2, + 3, + 2, + 4 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 4, + 4, + 3 + ], + [ + 4, + 2, + 4, + 4, + 3 + ], + [ + 4, + 2, + 4, + 4, + 3 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 2, + 1, + 5 + ], + [ + 4, + 2, + 2, + 1, + 5 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 1, + 3, + 4, + 2, + 5 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 4, + 1 + ], + [ + 1, + 4, + 3, + 4, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 1, + 2, + 5, + 4, + 5 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 3, + 3, + 2, + 5 + ], + [ + 3, + 3, + 2, + 5 + ], + [ + 3, + 3, + 2, + 5 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 5, + 1, + 3, + 4 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 2, + 4, + 5, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 5, + 3, + 2, + 4 + ], + [ + 3, + 5, + 3, + 2, + 4 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 2 + ], + [ + 4, + 1, + 5, + 1, + 2 + ], + [ + 4, + 1, + 5, + 2, + 1 + ], + [ + 4, + 1, + 5, + 2, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 3 + ], + [ + 5, + 5, + 4, + 1, + 3 + ], + [ + 5, + 5, + 4, + 1, + 3 + ], + [ + 5, + 5, + 4, + 1, + 3 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 1, + 3, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 4, + 1, + 4, + 3, + 5 + ], + [ + 4, + 1, + 4, + 3, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1, + 3 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4, + 1, + 2, + 3, + 3 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 4, + 4, + 5, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 5, + 2, + 5, + 3 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 2, + 5, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2, + 5 + ], + [ + 2, + 1, + 1, + 2, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 4, + 1, + 2, + 1 + ], + [ + 1, + 4, + 1, + 2, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 3, + 2, + 3, + 1 + ], + [ + 2, + 3, + 2, + 3, + 1 + ], + [ + 2, + 3, + 2, + 3, + 1 + ], + [ + 2, + 3, + 2, + 3, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 1, + 3 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 4, + 1, + 2 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 1, + 3, + 2, + 5 + ], + [ + 4, + 1, + 3, + 2, + 5 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4, + 4, + 1 + ], + [ + 5, + 3, + 4, + 4, + 1 + ], + [ + 5, + 3, + 4, + 4, + 3 + ], + [ + 5, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 4, + 5, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2 + ], + [ + 3, + 5, + 2, + 3 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 1, + 1, + 2, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 1, + 4, + 3 + ], + [ + 4, + 1, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 3, + 4, + 1, + 3 + ], + [ + 2, + 3, + 4, + 1, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 1, + 2, + 2, + 1 + ], + [ + 5, + 1, + 2, + 2, + 1 + ], + [ + 5, + 1, + 1, + 3, + 5 + ], + [ + 5, + 1, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 4 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2, + 5 + ], + [ + 1, + 5, + 1, + 2, + 5 + ], + [ + 1, + 5, + 3, + 2, + 1 + ], + [ + 1, + 5, + 3, + 2, + 1 + ], + [ + 1, + 5, + 3, + 1, + 1 + ], + [ + 1, + 1, + 4, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 4, + 5, + 4 + ], + [ + 1, + 3, + 4, + 5, + 4 + ], + [ + 1, + 3, + 4, + 5, + 4 + ], + [ + 1, + 3, + 4, + 5, + 4 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 4 + ], + [ + 5, + 4, + 4, + 1, + 4 + ], + [ + 5, + 4, + 4, + 1, + 4 + ], + [ + 5, + 4, + 4, + 1, + 4 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4, + 3, + 4 + ], + [ + 3, + 3, + 4, + 3, + 4 + ], + [ + 3, + 3, + 4, + 3, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5, + 4, + 5, + 2, + 2 + ], + [ + 5, + 4, + 5, + 2, + 2 + ], + [ + 5, + 4, + 5, + 2, + 2 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 4, + 5, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 2, + 5, + 5, + 3, + 2 + ], + [ + 2, + 5, + 5, + 3, + 2 + ], + [ + 2, + 1, + 4, + 5, + 1 + ], + [ + 2, + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 2, + 4, + 2, + 3 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 4, + 2, + 4, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 5, + 5, + 5 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 4, + 2, + 4, + 2, + 4 + ], + [ + 4, + 2, + 4, + 2, + 4 + ], + [ + 4, + 2, + 4, + 2, + 4 + ], + [ + 4, + 2, + 4, + 2, + 4 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 3, + 5, + 2, + 5, + 1 + ], + [ + 4, + 2, + 1, + 4, + 4 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 4, + 3, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4, + 3, + 1, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 3, + 4, + 1, + 5 + ], + [ + 4, + 3, + 4, + 1, + 5 + ], + [ + 4, + 3, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 2, + 1 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 2, + 4, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 2, + 5, + 3 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 3, + 5, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 4, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 5, + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 3, + 3, + 5 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 4, + 3, + 2 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 1, + 3, + 2 + ], + [ + 2, + 3, + 1, + 3, + 2 + ], + [ + 2, + 3, + 1, + 3, + 2 + ], + [ + 2, + 3, + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5, + 4, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 4, + 3 + ], + [ + 2, + 2, + 1, + 4, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 3, + 5, + 3 + ], + [ + 1, + 3, + 5, + 3 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 5, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3, + 5, + 3, + 4, + 3 + ], + [ + 3, + 5, + 3, + 4, + 3 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 1, + 4, + 1, + 5, + 2 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 5, + 5, + 4, + 2, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3, + 1 + ], + [ + 3, + 3, + 5, + 2, + 3 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 2, + 5, + 3, + 5 + ], + [ + 2, + 2, + 5, + 3, + 5 + ], + [ + 2, + 2, + 5, + 3, + 5 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 3 + ], + [ + 2, + 2, + 5, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 4, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 4, + 4, + 5, + 3, + 5 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 4, + 4 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 5, + 3, + 4, + 3 + ], + [ + 4, + 5, + 3, + 4, + 3 + ], + [ + 4, + 5, + 3, + 4, + 3 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5, + 2, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5, + 4, + 2, + 5, + 2 + ], + [ + 5, + 4, + 2, + 5, + 2 + ], + [ + 5, + 4, + 2, + 5, + 2 + ], + [ + 5, + 4, + 2, + 5, + 2 + ], + [ + 5, + 4, + 2, + 5, + 2 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 1, + 5, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 2, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 4, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4, + 2, + 1, + 3, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 3, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 5 + ], + [ + 2, + 1, + 2, + 1, + 5 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 4, + 3, + 2, + 1, + 5 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 1, + 2, + 4, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 3 + ], + [ + 1, + 4, + 3, + 3 + ], + [ + 1, + 4, + 3, + 3 + ], + [ + 1, + 4, + 3, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4, + 3 + ], + [ + 4, + 1, + 3, + 4, + 3 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 4, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 5, + 5, + 3 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 5, + 2 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 5, + 2 + ], + [ + 1, + 1, + 2, + 4, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 5, + 4, + 2 + ], + [ + 5, + 4, + 5, + 4, + 2 + ], + [ + 5, + 4, + 5, + 4, + 2 + ], + [ + 5, + 4, + 5, + 4, + 2 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 4, + 5, + 5, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 4, + 5, + 5 + ], + [ + 5, + 4, + 5, + 5 + ], + [ + 5, + 4, + 5, + 5 + ], + [ + 5, + 4, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 4 + ], + [ + 2, + 2, + 1, + 1, + 4 + ], + [ + 2, + 2, + 1, + 1, + 4 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 5 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 1, + 4, + 3, + 2 + ], + [ + 3, + 1, + 4, + 3, + 2 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1, + 3, + 5, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 2, + 3, + 5, + 3 + ], + [ + 2, + 3, + 5, + 3 + ], + [ + 2, + 3, + 5, + 3 + ], + [ + 2, + 3, + 5, + 3 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 1, + 1, + 3, + 5, + 4 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 4 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 2, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 3, + 1, + 4, + 2 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1, + 3, + 2, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 2, + 2 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 5, + 2, + 2, + 4, + 5 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 5, + 3, + 5, + 2 + ], + [ + 3, + 5, + 3, + 5, + 1 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 1 + ], + [ + 3, + 1, + 5, + 3, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 2, + 4 + ], + [ + 2, + 3, + 2, + 4 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 4, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 3 + ], + [ + 5, + 1, + 1, + 4, + 3 + ], + [ + 5, + 1, + 1, + 4, + 3 + ], + [ + 5, + 1, + 1, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 3, + 4, + 2, + 3 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 1, + 5, + 2 + ], + [ + 3, + 1, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4, + 4 + ], + [ + 4, + 2, + 1, + 4, + 4 + ], + [ + 4, + 2, + 1, + 4, + 4 + ], + [ + 4, + 2, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 4 + ], + [ + 3, + 5, + 3, + 5, + 4 + ], + [ + 3, + 5, + 3, + 5, + 4 + ], + [ + 3, + 5, + 3, + 5, + 5 + ], + [ + 3, + 5, + 3, + 5, + 5 + ], + [ + 3, + 5, + 3, + 5, + 1 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 5, + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 5, + 1, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 4, + 2, + 4, + 1 + ], + [ + 5, + 4, + 2, + 4, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 1 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 3, + 5, + 2, + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 2, + 4 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 5, + 2, + 2 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1, + 3, + 1, + 2 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 3, + 3, + 1 + ], + [ + 5, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 2, + 2, + 5 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 4, + 1, + 5, + 5, + 1 + ], + [ + 4, + 1, + 5, + 5, + 1 + ], + [ + 4, + 1, + 5, + 5, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 3, + 4, + 2, + 2 + ], + [ + 3, + 3, + 4, + 2, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 2, + 5, + 2, + 3 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 5, + 2, + 5, + 3, + 3 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 5, + 2, + 5, + 3, + 1 + ], + [ + 2, + 1, + 5, + 2, + 5 + ], + [ + 2, + 1, + 5, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 5 + ], + [ + 4, + 1, + 3, + 1, + 5 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 3, + 3 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1 + ], + [ + 4, + 5, + 2, + 4, + 2 + ], + [ + 4, + 5, + 2, + 4, + 2 + ], + [ + 4, + 5, + 2, + 4, + 2 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 4, + 1 + ], + [ + 2, + 1, + 2, + 4, + 1 + ], + [ + 2, + 1, + 2, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 3, + 2, + 5, + 2, + 5 + ], + [ + 3, + 2, + 5, + 2, + 5 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 4, + 5, + 2, + 3, + 2 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 1, + 1, + 3, + 3, + 2 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 3, + 3, + 3, + 4 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 2, + 2, + 5, + 3 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 2, + 4, + 5, + 3 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 5, + 2, + 5, + 1 + ], + [ + 4, + 5, + 2, + 5, + 1 + ], + [ + 4, + 5, + 2, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 2, + 4, + 2, + 5, + 5 + ], + [ + 2, + 4, + 2, + 5, + 5 + ], + [ + 2, + 4, + 2, + 5, + 5 + ], + [ + 2, + 4, + 2, + 5, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 4 + ], + [ + 4, + 2, + 3, + 4 + ], + [ + 4, + 2, + 3, + 4 + ], + [ + 4, + 2, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 2, + 4 + ], + [ + 2, + 4, + 1, + 2, + 4 + ], + [ + 2, + 4, + 1, + 2, + 4 + ], + [ + 1, + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 4, + 5, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 5 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 3, + 3 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 4, + 5, + 5 + ], + [ + 4, + 2, + 4, + 5, + 5 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 3, + 4 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 1, + 2, + 5, + 2, + 1 + ], + [ + 1, + 2, + 5, + 2, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 3, + 4, + 4, + 2 + ], + [ + 1, + 3, + 4, + 4, + 2 + ], + [ + 1, + 3, + 4, + 4, + 2 + ], + [ + 1, + 3, + 4, + 4, + 1 + ], + [ + 1, + 3, + 4, + 4, + 1 + ], + [ + 1, + 3, + 4, + 4, + 1 + ], + [ + 1, + 3, + 4, + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 3, + 4, + 3 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 5, + 2, + 2, + 5 + ], + [ + 5, + 2, + 2, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 5, + 2, + 4, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 4, + 5, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 4, + 3, + 5, + 4, + 2 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 3, + 4, + 1, + 5 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 4 + ], + [ + 3, + 2, + 4, + 4 + ], + [ + 3, + 2, + 4, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 1, + 2, + 1, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 4, + 2, + 2 + ], + [ + 2, + 3, + 4, + 2, + 1 + ], + [ + 2, + 3, + 4, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 5, + 4, + 4, + 2, + 4 + ], + [ + 5, + 4, + 1, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1, + 3 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 2, + 5 + ], + [ + 2, + 3, + 3, + 1, + 2 + ], + [ + 2, + 3, + 3, + 1, + 2 + ], + [ + 2, + 3, + 3, + 1, + 2 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4, + 4, + 3, + 2, + 3 + ], + [ + 4, + 4, + 3, + 2, + 3 + ], + [ + 4, + 4, + 3, + 2, + 3 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 4, + 4, + 3, + 2 + ], + [ + 2, + 5, + 1, + 2, + 1 + ], + [ + 2, + 5, + 1, + 2, + 1 + ], + [ + 2, + 5, + 1, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 2, + 1, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3, + 3, + 2 + ], + [ + 4, + 3, + 3, + 3, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 4 + ], + [ + 4, + 5, + 5, + 4 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 4, + 5, + 4, + 3 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1, + 2, + 2 + ], + [ + 3, + 1, + 2, + 2 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3, + 3 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 5, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 1, + 4, + 3, + 4 + ], + [ + 4, + 1, + 4, + 3, + 4 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 3, + 2, + 2 + ], + [ + 2, + 3, + 2, + 2 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 5, + 2, + 3, + 2 + ], + [ + 3, + 5, + 2, + 3, + 2 + ], + [ + 3, + 5, + 2, + 3, + 2 + ], + [ + 3, + 5, + 2, + 3, + 2 + ], + [ + 3, + 5, + 2, + 3, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2, + 5, + 4 + ], + [ + 4, + 5, + 2, + 5, + 4 + ], + [ + 4, + 5, + 2, + 5, + 4 + ], + [ + 4, + 5, + 2, + 5, + 4 + ], + [ + 4, + 5, + 2, + 5, + 1 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 5, + 1, + 2, + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 2, + 3, + 2, + 5, + 4 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 2, + 3, + 3, + 2 + ], + [ + 2, + 2, + 3, + 3, + 2 + ], + [ + 2, + 2, + 3, + 1, + 3 + ], + [ + 2, + 2, + 3, + 1, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 4, + 4 + ], + [ + 5, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 2, + 5 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 1, + 2, + 3, + 4, + 4 + ], + [ + 1, + 2, + 3, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 1, + 2, + 1, + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 5, + 4, + 2 + ], + [ + 1, + 1, + 5, + 4, + 2 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 5, + 2, + 4, + 1 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 1, + 3, + 2 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 4, + 1, + 3 + ], + [ + 3, + 1, + 4, + 5, + 1 + ], + [ + 3, + 1, + 4, + 5, + 2 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 2, + 5, + 4 + ], + [ + 3, + 1, + 2, + 5, + 4 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 5, + 1 + ], + [ + 1, + 1, + 3, + 5, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 4, + 5, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 5, + 5 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 3, + 1, + 3, + 2 + ], + [ + 5, + 3, + 1, + 3, + 2 + ], + [ + 5, + 3, + 1, + 1, + 4 + ], + [ + 5, + 3, + 1, + 1, + 4 + ], + [ + 5, + 3, + 1, + 1, + 4 + ], + [ + 5, + 3, + 1, + 1, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 2, + 1 + ], + [ + 1, + 3, + 3, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3, + 4 + ], + [ + 1, + 5, + 5, + 3, + 4 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 3, + 5 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 4, + 1, + 5, + 4 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 4, + 2, + 1, + 5 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 4 + ], + [ + 3, + 4, + 3, + 1, + 4 + ], + [ + 3, + 4, + 3, + 1, + 4 + ], + [ + 3, + 4, + 3, + 1, + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 5, + 4, + 3 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5, + 1, + 1 + ], + [ + 1, + 4, + 5, + 1, + 1 + ], + [ + 1, + 4, + 5, + 1, + 1 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 1 + ], + [ + 2, + 4, + 4, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 4, + 3, + 4, + 3, + 3 + ], + [ + 4, + 3, + 4, + 3, + 3 + ], + [ + 4, + 3, + 4, + 3, + 1 + ], + [ + 4, + 3, + 4, + 3, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 2, + 2, + 2, + 3 + ], + [ + 2, + 2, + 2, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 1, + 4, + 5, + 3 + ], + [ + 1, + 4, + 5, + 3 + ], + [ + 1, + 4, + 5, + 3 + ], + [ + 1, + 4, + 5, + 3 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 2, + 3, + 5, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3, + 3 + ], + [ + 3, + 4, + 1, + 3, + 3 + ], + [ + 3, + 4, + 1, + 3, + 3 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 4, + 5, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 3, + 1, + 5 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 3, + 2, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 2, + 3, + 4, + 1, + 2 + ], + [ + 2, + 3, + 4, + 1, + 2 + ], + [ + 2, + 3, + 4, + 1, + 2 + ], + [ + 2, + 3, + 4, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 2, + 5, + 1 + ], + [ + 5, + 3, + 1, + 1, + 5 + ], + [ + 5, + 3, + 4, + 3, + 4 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 2, + 2, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3, + 5 + ], + [ + 1, + 3, + 2, + 3, + 5 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 4, + 3 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 4, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 5, + 2, + 4 + ], + [ + 4, + 5, + 5, + 2, + 4 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 2, + 2, + 5, + 2 + ], + [ + 3, + 2, + 2, + 5, + 2 + ], + [ + 3, + 2, + 2, + 5, + 2 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 2, + 2, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 2 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 3, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 5 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 4, + 2, + 1 + ], + [ + 3, + 1, + 4, + 2, + 1 + ], + [ + 3, + 1, + 4, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 4, + 3, + 3, + 4 + ], + [ + 1, + 4, + 3, + 3, + 4 + ], + [ + 1, + 4, + 3, + 3, + 1 + ], + [ + 1, + 4, + 1, + 3, + 3 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 1, + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 5, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 3, + 2 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 2, + 5, + 3 + ], + [ + 4, + 4, + 2, + 5, + 3 + ], + [ + 4, + 4, + 2, + 5, + 3 + ], + [ + 4, + 4, + 2, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 3, + 3 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 3, + 2 + ], + [ + 5, + 5, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1, + 4 + ], + [ + 3, + 3, + 1, + 4 + ], + [ + 3, + 3, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 2, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 2, + 3, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 2, + 5, + 3 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 3, + 2, + 1 + ], + [ + 2, + 4, + 3, + 2, + 1 + ], + [ + 2, + 4, + 3, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1, + 5 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 5, + 5, + 2, + 4, + 2 + ], + [ + 5, + 5, + 2, + 4, + 2 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 5, + 4, + 4, + 3 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 4, + 1, + 4, + 4, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 4, + 3 + ], + [ + 2, + 1, + 2, + 4, + 3 + ], + [ + 2, + 1, + 2, + 4, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 4, + 4, + 5, + 5 + ], + [ + 5, + 4, + 4, + 5, + 5 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 5, + 5, + 2, + 4 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3 + ], + [ + 5, + 5, + 2, + 4, + 3 + ], + [ + 5, + 5, + 2, + 4, + 3 + ], + [ + 5, + 5, + 2, + 4, + 3 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 5, + 2, + 1 + ], + [ + 5, + 2, + 5, + 2, + 1 + ], + [ + 5, + 2, + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 4, + 3, + 2, + 3, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 3, + 4, + 4 + ], + [ + 2, + 5, + 3, + 4, + 4 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 5, + 4, + 2 + ], + [ + 3, + 1, + 5, + 4, + 1 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 3, + 1, + 1, + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5, + 4, + 4 + ], + [ + 2, + 2, + 5, + 4, + 1 + ], + [ + 2, + 2, + 5, + 4, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 5, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 5, + 5, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 2, + 4, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 2, + 1, + 1, + 3 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 5 + ], + [ + 1, + 3, + 2, + 5 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 5, + 3, + 2 + ], + [ + 1, + 5, + 3, + 2 + ], + [ + 1, + 5, + 3, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 2, + 3, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 4, + 2, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3, + 5, + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 1, + 3, + 2 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 3, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 1, + 1, + 2 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 4, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 5, + 3, + 4 + ], + [ + 4, + 5, + 3, + 4 + ], + [ + 4, + 5, + 3, + 4 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 5, + 2, + 2, + 2, + 5 + ], + [ + 5, + 2, + 2, + 2, + 5 + ], + [ + 5, + 2, + 2, + 2, + 5 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5, + 5, + 3, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 5, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 4 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 3 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 2 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 2, + 3, + 4, + 5 + ], + [ + 2, + 3, + 4, + 5 + ], + [ + 2, + 3, + 4, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 1, + 3, + 3, + 5 + ], + [ + 1, + 3, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 3, + 3 + ], + [ + 3, + 4, + 4, + 3, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4, + 5, + 4 + ], + [ + 3, + 3, + 4, + 5, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 5 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 2, + 4, + 1 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 5, + 1 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 3, + 1, + 2 + ], + [ + 2, + 5, + 3, + 1, + 2 + ], + [ + 2, + 5, + 3, + 1, + 2 + ], + [ + 2, + 5, + 3, + 1, + 2 + ], + [ + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 4, + 3, + 2 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 2, + 5, + 3, + 1 + ], + [ + 1, + 2, + 5, + 3, + 1 + ], + [ + 1, + 2, + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 3, + 4, + 3 + ], + [ + 4, + 4, + 3, + 4, + 3 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 5 + ], + [ + 4, + 3, + 5, + 1 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 2, + 4, + 3 + ], + [ + 2, + 3, + 2, + 4, + 3 + ], + [ + 2, + 3, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 4, + 2, + 5, + 2, + 2 + ], + [ + 4, + 2, + 5, + 2, + 2 + ], + [ + 4, + 2, + 5, + 2, + 2 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 5 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 5, + 5 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 1, + 5, + 2, + 1 + ], + [ + 3, + 1, + 5, + 3, + 3 + ], + [ + 3, + 1, + 5, + 3, + 3 + ], + [ + 3, + 1, + 5, + 3, + 3 + ], + [ + 3, + 1, + 5, + 3, + 3 + ], + [ + 3, + 1, + 5, + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 1, + 1, + 5 + ], + [ + 1, + 4, + 1, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 3, + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 5, + 4, + 4, + 5, + 4 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 2, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 2, + 2 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 4, + 5, + 4 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 3, + 2, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 5, + 2 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 4, + 4 + ], + [ + 5, + 3, + 4, + 4 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 3, + 4, + 5, + 2 + ], + [ + 5, + 3, + 4, + 5, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 2, + 2, + 5, + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 2, + 3, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 5, + 3, + 5, + 4 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 1, + 2, + 4 + ], + [ + 2, + 5, + 1, + 2, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 2, + 3, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 3 + ], + [ + 1, + 2, + 1, + 4, + 3 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 1, + 2, + 2, + 1, + 4 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 5 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 2 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 4 + ], + [ + 1, + 2, + 2, + 3, + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 3, + 4, + 2 + ], + [ + 2, + 5, + 3, + 4, + 1 + ], + [ + 2, + 5, + 3, + 4, + 1 + ], + [ + 2, + 5, + 3, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 2, + 5, + 1, + 1, + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 5, + 5, + 3 + ], + [ + 5, + 4, + 5, + 5, + 3 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 3, + 3, + 2 + ], + [ + 1, + 4, + 3, + 3, + 2 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 3, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 3, + 2 + ], + [ + 5, + 1, + 2, + 3, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 5, + 4, + 3 + ], + [ + 1, + 1, + 5, + 4, + 4 + ], + [ + 1, + 1, + 5, + 4, + 2 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 5, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 4, + 1, + 2, + 3, + 3 + ], + [ + 4, + 1, + 2, + 4, + 3 + ], + [ + 4, + 1, + 2, + 4, + 3 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 2, + 4, + 5, + 3, + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 4, + 2 + ], + [ + 1, + 5, + 1, + 1, + 4 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 5, + 5 + ], + [ + 5, + 1, + 1, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 5, + 3 + ], + [ + 2, + 1, + 4, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 3, + 4, + 5 + ], + [ + 5, + 4, + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 3, + 5, + 2, + 2, + 5 + ], + [ + 3, + 5, + 2, + 2, + 5 + ], + [ + 3, + 5, + 2, + 2, + 5 + ], + [ + 3, + 5, + 2, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 3, + 5, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 2, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 4, + 4 + ], + [ + 1, + 1, + 4, + 4 + ], + [ + 1, + 1, + 4, + 4 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 4, + 4 + ], + [ + 1, + 4, + 1, + 4, + 4 + ], + [ + 1, + 4, + 1, + 4, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 3, + 3, + 4, + 2, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 3, + 3, + 4, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 4, + 1, + 3, + 5 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 5 + ], + [ + 2, + 1, + 4, + 5 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 3, + 3, + 4, + 2, + 5 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 3 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 5, + 2 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 4, + 2, + 2 + ], + [ + 4, + 4, + 2, + 2 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 4, + 3, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 2, + 5, + 4, + 3, + 4 + ], + [ + 2, + 5, + 4, + 3, + 4 + ], + [ + 2, + 5, + 4, + 3, + 4 + ], + [ + 2, + 5, + 4, + 3, + 1 + ], + [ + 2, + 5, + 4, + 3, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 5, + 2, + 5 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1, + 4, + 3, + 1 + ], + [ + 5, + 1, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 2, + 1, + 5, + 4 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 4, + 2, + 2, + 3, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 2, + 3, + 3, + 5, + 3 + ], + [ + 2, + 3, + 3, + 5, + 3 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 4, + 3 + ], + [ + 5, + 4, + 2, + 4, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 4, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 4, + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4, + 4 + ], + [ + 4, + 1, + 1, + 4, + 4 + ], + [ + 4, + 1, + 1, + 4, + 4 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 4, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 5, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 5, + 5, + 4 + ], + [ + 1, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 3, + 1 + ], + [ + 2, + 5, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 2, + 4, + 2, + 3, + 2 + ], + [ + 2, + 4, + 2, + 3, + 2 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 3, + 4, + 5 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 5 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 2 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 4 + ], + [ + 2, + 4, + 3, + 5, + 5 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 3 + ], + [ + 2, + 3, + 1, + 1, + 3 + ], + [ + 2, + 3, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 2, + 5, + 4 + ], + [ + 2, + 1, + 2, + 5, + 4 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 1, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 2 + ], + [ + 5, + 2, + 1, + 1, + 2 + ], + [ + 5, + 2, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 4, + 2 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 5, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 5, + 3, + 2 + ], + [ + 5, + 3, + 5, + 3, + 2 + ], + [ + 5, + 3, + 5, + 3, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 3, + 2 + ], + [ + 2, + 4, + 3, + 2 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 4, + 3 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 3, + 5, + 5 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 5 + ], + [ + 4, + 1, + 1, + 5, + 5 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 5, + 2, + 2, + 1, + 3 + ], + [ + 5, + 2, + 2, + 1, + 3 + ], + [ + 5, + 2, + 2, + 1, + 3 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 3, + 1, + 5, + 2 + ], + [ + 1, + 3, + 1, + 5, + 2 + ], + [ + 1, + 3, + 1, + 5, + 2 + ], + [ + 1, + 3, + 1, + 5, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 5, + 4, + 2, + 3, + 4 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 5, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 5, + 3 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 3, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 5, + 3, + 3 + ], + [ + 3, + 4, + 5, + 3, + 3 + ], + [ + 3, + 4, + 5, + 3, + 3 + ], + [ + 3, + 1, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 2, + 3 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 3, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 3, + 5, + 4 + ], + [ + 2, + 5, + 3, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 2, + 1 + ], + [ + 2, + 5, + 1, + 2, + 1 + ], + [ + 2, + 5, + 5, + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5, + 4, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 3, + 5, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 5, + 2, + 1 + ], + [ + 1, + 3, + 5, + 2, + 1 + ], + [ + 1, + 3, + 5, + 2, + 1 + ], + [ + 1, + 3, + 5, + 2, + 1 + ], + [ + 1, + 3, + 5, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 3, + 3, + 4, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 2, + 4 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 5, + 5, + 3, + 3 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 4, + 5, + 1, + 4 + ], + [ + 2, + 4, + 5, + 1, + 4 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 5, + 5, + 4, + 5, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3, + 2, + 4 + ], + [ + 4, + 2, + 3, + 2, + 4 + ], + [ + 4, + 2, + 3, + 2, + 4 + ], + [ + 4, + 2, + 3, + 2, + 4 + ], + [ + 4, + 2, + 1, + 3 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 5, + 4, + 3, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 4, + 2 + ], + [ + 5, + 1, + 4, + 4, + 2 + ], + [ + 5, + 1, + 4, + 4, + 2 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 3, + 1, + 5, + 4 + ], + [ + 5, + 3, + 1, + 5, + 4 + ], + [ + 5, + 3, + 1, + 5, + 4 + ], + [ + 5, + 3, + 1, + 5, + 4 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 2, + 2, + 2, + 2 + ], + [ + 5, + 2, + 2, + 1, + 2 + ], + [ + 5, + 2, + 5, + 1, + 2 + ], + [ + 5, + 2, + 5, + 1, + 2 + ], + [ + 5, + 2, + 5, + 1, + 2 + ], + [ + 5, + 2, + 5, + 1, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 3, + 2, + 4 + ], + [ + 3, + 3, + 2, + 4 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 1, + 5 + ], + [ + 4 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 3, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 2, + 4, + 2, + 3 + ], + [ + 3, + 2, + 4, + 2, + 3 + ], + [ + 3, + 2, + 4, + 2, + 1 + ], + [ + 3, + 2, + 4, + 2, + 1 + ], + [ + 3, + 2, + 4, + 2, + 3 + ], + [ + 3, + 2, + 4, + 2, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 4, + 2, + 2 + ], + [ + 1, + 2, + 4, + 2, + 2 + ], + [ + 1, + 2, + 4, + 2, + 2 + ], + [ + 1, + 2, + 4, + 2, + 1 + ], + [ + 1, + 2, + 4, + 2, + 1 + ], + [ + 1, + 2, + 4, + 2, + 1 + ], + [ + 1, + 2, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 4, + 3, + 4, + 4, + 1 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 5, + 3, + 4, + 4 + ], + [ + 1, + 5, + 3, + 4, + 1 + ], + [ + 1, + 5, + 3, + 4, + 1 + ], + [ + 1, + 5, + 3, + 4, + 1 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 1, + 5, + 1, + 2, + 3 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 2, + 2 + ], + [ + 2, + 4, + 2, + 5 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 3, + 5, + 4 + ], + [ + 2, + 1, + 3, + 5, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 4, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5, + 5, + 5, + 3 + ], + [ + 3, + 5, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 5, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2, + 4, + 3, + 4 + ], + [ + 2, + 2, + 4, + 3, + 4 + ], + [ + 2, + 2, + 4, + 3, + 4 + ], + [ + 2, + 2, + 4, + 3, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 5, + 5 + ], + [ + 2, + 2, + 1, + 5, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1, + 3, + 3, + 2 + ], + [ + 1, + 1, + 3, + 3, + 2 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 2, + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 2, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 2, + 3, + 1 + ], + [ + 3, + 2, + 2, + 3, + 1 + ], + [ + 3, + 2, + 2, + 3, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 1, + 4, + 4 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5, + 2, + 4 + ], + [ + 1, + 4, + 5, + 2, + 1 + ], + [ + 1, + 4, + 5, + 2, + 1 + ], + [ + 1, + 4, + 5, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 4, + 3, + 2, + 5, + 3 + ], + [ + 4, + 3, + 2, + 5, + 3 + ], + [ + 4, + 3, + 2, + 5, + 3 + ], + [ + 4, + 3, + 2, + 5, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 4, + 5, + 2 + ], + [ + 1, + 5, + 4, + 5, + 2 + ], + [ + 1, + 5, + 4, + 5, + 2 + ], + [ + 1, + 5, + 4, + 5, + 2 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 1, + 5, + 4, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 1, + 1, + 5, + 1, + 3 + ], + [ + 5, + 5, + 2, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 5, + 4, + 4, + 1, + 2 + ], + [ + 1, + 3, + 2, + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 3, + 4, + 3, + 5 + ], + [ + 2, + 3, + 4, + 3, + 5 + ], + [ + 2, + 3, + 4, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 1 + ], + [ + 3, + 2, + 1, + 4, + 1 + ], + [ + 3, + 1, + 3, + 5, + 2 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 2, + 1, + 2, + 4, + 4 + ], + [ + 5, + 2, + 1, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1, + 5 + ], + [ + 5, + 1, + 5, + 1, + 3 + ], + [ + 5, + 1, + 5, + 1, + 3 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 5, + 5 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 4, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 4, + 3, + 5 + ], + [ + 5, + 2, + 4, + 3, + 5 + ], + [ + 5, + 2, + 4, + 3, + 5 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1, + 4, + 1, + 5 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 2, + 5 + ], + [ + 3, + 1, + 2, + 2, + 5 + ], + [ + 3, + 1, + 2, + 2, + 5 + ], + [ + 3, + 1, + 2, + 2, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 5, + 3, + 1, + 2 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 1, + 3 + ], + [ + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 2, + 4, + 3 + ], + [ + 5, + 2, + 2, + 4, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2, + 4, + 5, + 4 + ], + [ + 5, + 2, + 4, + 5, + 4 + ], + [ + 5, + 2, + 4, + 5, + 4 + ], + [ + 5, + 2, + 4, + 5, + 4 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 5, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 5, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 5, + 1 + ], + [ + 3, + 4, + 3, + 5, + 1 + ], + [ + 3, + 4, + 3, + 5, + 1 + ], + [ + 3, + 4, + 3, + 5, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 4, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 5, + 3, + 4, + 3 + ], + [ + 2, + 5, + 3, + 4, + 3 + ], + [ + 2, + 5, + 3, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1, + 2 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 5, + 2 + ], + [ + 2, + 1, + 1, + 5, + 2 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 1, + 4, + 3, + 2 + ], + [ + 4, + 1, + 4, + 3, + 2 + ], + [ + 4, + 1, + 4, + 3, + 2 + ], + [ + 4, + 1, + 4, + 3, + 2 + ], + [ + 4, + 1, + 4, + 3, + 5 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 4, + 4, + 1, + 5 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 3, + 2, + 3 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 1, + 3, + 2 + ], + [ + 2, + 3, + 4, + 1, + 5 + ], + [ + 2, + 3, + 4, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1, + 1 + ], + [ + 5, + 4, + 3, + 4, + 4 + ], + [ + 5, + 4, + 3, + 4, + 4 + ], + [ + 5, + 4, + 3, + 4, + 4 + ], + [ + 5, + 4, + 3, + 4, + 5 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 5, + 5, + 3, + 5, + 5 + ], + [ + 5, + 5, + 3, + 5, + 5 + ], + [ + 5, + 5, + 3, + 5, + 3 + ], + [ + 5, + 5, + 3, + 5, + 3 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5, + 5, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 3 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 5, + 1, + 4, + 1 + ], + [ + 5, + 5, + 1, + 4, + 1 + ], + [ + 5, + 5, + 1, + 4, + 1 + ], + [ + 5, + 5, + 1, + 4, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 2, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 1, + 5, + 3 + ], + [ + 2, + 3, + 1, + 5, + 3 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 3 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 3, + 3, + 3 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 2, + 2, + 3, + 5 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 2, + 4, + 5, + 1, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 5, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 5, + 3, + 5, + 5, + 4 + ], + [ + 5, + 3, + 5, + 5, + 4 + ], + [ + 5, + 3, + 5, + 5, + 4 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 4 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 3, + 2 + ], + [ + 5, + 3, + 5, + 3, + 2 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 5 + ], + [ + 5, + 3, + 5, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 3, + 1, + 4 + ], + [ + 1, + 2, + 4, + 1, + 4 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 2, + 4, + 3, + 3, + 2 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 1, + 3, + 1 + ], + [ + 2, + 4, + 1, + 3, + 1 + ], + [ + 1, + 1, + 4, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 4, + 1, + 5, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 1 + ], + [ + 2, + 3, + 3, + 2, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 3, + 2, + 5, + 1 + ], + [ + 2, + 3, + 2, + 5, + 1 + ], + [ + 1, + 1, + 3, + 2, + 3 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 1, + 3, + 2, + 5 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 5, + 3 + ], + [ + 3, + 4, + 4, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 4 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 5, + 2, + 5, + 1 + ], + [ + 2, + 5, + 2, + 5, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 4, + 2, + 1 + ], + [ + 1, + 5, + 4, + 2, + 1 + ], + [ + 1, + 5, + 4, + 2, + 1 + ], + [ + 1, + 5, + 4, + 2, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 1, + 5 + ], + [ + 1, + 5, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 3, + 4, + 5 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 1, + 4, + 5, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 1, + 4, + 3, + 4, + 4 + ], + [ + 1, + 4, + 3, + 4, + 4 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 2, + 5, + 4, + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 4, + 2 + ], + [ + 2, + 3, + 4, + 2 + ], + [ + 2, + 3, + 4, + 2 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 5, + 2 + ], + [ + 2, + 5, + 5, + 2 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 3, + 4, + 2 + ], + [ + 1, + 2, + 3, + 4, + 1 + ], + [ + 1, + 2, + 3, + 4, + 1 + ], + [ + 1, + 2, + 3, + 4, + 1 + ], + [ + 1, + 2, + 3, + 4, + 1 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 3, + 1, + 3, + 5 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 2, + 4, + 4, + 5 + ], + [ + 1, + 2, + 4, + 4, + 5 + ], + [ + 1, + 2, + 4, + 4, + 1 + ], + [ + 1, + 2, + 4, + 3, + 1 + ], + [ + 1, + 2, + 4, + 3, + 1 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 4, + 2, + 1, + 3, + 1 + ], + [ + 2, + 4, + 1, + 3, + 3 + ], + [ + 2, + 4, + 1, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 4, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3, + 3, + 5 + ], + [ + 3, + 1, + 3, + 3, + 5 + ], + [ + 3, + 1, + 3, + 1, + 3 + ], + [ + 3, + 1, + 3, + 1, + 3 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 2, + 3 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 4, + 2, + 3, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 3, + 4, + 3 + ], + [ + 5, + 1, + 3, + 4, + 3 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 2, + 5, + 3, + 5, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 3, + 3 + ], + [ + 3, + 2, + 4, + 3, + 3 + ], + [ + 3, + 2, + 4, + 3, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 5, + 5 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 3, + 2 + ], + [ + 2, + 2, + 4, + 3, + 2 + ], + [ + 2, + 2, + 4, + 3, + 2 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 3 + ], + [ + 4, + 5, + 5, + 1, + 3 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 3, + 3, + 5, + 4 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 5, + 4, + 3 + ], + [ + 3, + 3, + 5, + 4, + 3 + ], + [ + 3, + 3, + 5, + 4, + 3 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 2, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 4, + 5 + ], + [ + 2, + 1, + 4, + 5 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 1, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1 + ], + [ + 2, + 1, + 1, + 2, + 5 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 3, + 1, + 2 + ], + [ + 5, + 3, + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 2, + 2, + 2, + 3 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 4, + 1 + ], + [ + 4, + 1, + 5, + 4, + 1 + ], + [ + 4, + 1, + 5, + 4, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 4, + 1, + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 3, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 3, + 2, + 5 + ], + [ + 2, + 3, + 1, + 5, + 3 + ], + [ + 2, + 3, + 1, + 5, + 3 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 5, + 5, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 5, + 2, + 5 + ], + [ + 1, + 4, + 4, + 2, + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 3, + 2, + 5 + ], + [ + 5, + 3, + 2, + 5 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 4 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 4, + 4, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4, + 3, + 3, + 5 + ], + [ + 2, + 4, + 3, + 3, + 5 + ], + [ + 2, + 4, + 3, + 3, + 5 + ], + [ + 2, + 4, + 3, + 3, + 5 + ], + [ + 2, + 4, + 3, + 3, + 5 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 2, + 2, + 4, + 3, + 5 + ], + [ + 2, + 2, + 4, + 3, + 5 + ], + [ + 2, + 2, + 4, + 3, + 5 + ], + [ + 2, + 2, + 4, + 3, + 5 + ], + [ + 2, + 2, + 4, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 3, + 4 + ], + [ + 5, + 2, + 3, + 4 + ], + [ + 5, + 2, + 3, + 4 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 4, + 2 + ], + [ + 5, + 5, + 3, + 1, + 1 + ], + [ + 5, + 5, + 3, + 3, + 1 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 3, + 5 + ], + [ + 5, + 1, + 2, + 3, + 5 + ], + [ + 5, + 1, + 2, + 3, + 1 + ], + [ + 5, + 1, + 2, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 1, + 2, + 4, + 1, + 5 + ], + [ + 1, + 2, + 4, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 5, + 4, + 4, + 5 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 5, + 4, + 5 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 4, + 5, + 5, + 5 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 4, + 5 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 4, + 3 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 2, + 2 + ], + [ + 2, + 5, + 1, + 2, + 2 + ], + [ + 2, + 5, + 1, + 2, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 4, + 4, + 2, + 5 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 5, + 5 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 2, + 4, + 1, + 4, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 3, + 4, + 5 + ], + [ + 2, + 4, + 3, + 4, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 4, + 2, + 4 + ], + [ + 3, + 1, + 4, + 2, + 4 + ], + [ + 3, + 1, + 4, + 2, + 4 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 4, + 4, + 2 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 2, + 1, + 2 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 4, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 3, + 4, + 1 + ], + [ + 3, + 2, + 3, + 4, + 3 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 2, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2, + 2 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 4, + 2, + 1, + 1 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 3, + 4, + 4, + 2, + 5 + ], + [ + 3, + 4, + 4, + 2, + 5 + ], + [ + 3, + 4, + 4, + 2, + 5 + ], + [ + 3, + 4, + 4, + 2, + 5 + ], + [ + 3, + 4, + 4, + 2, + 5 + ], + [ + 3, + 4, + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 2, + 1, + 2, + 5 + ], + [ + 2, + 1, + 2, + 5 + ], + [ + 2, + 1, + 2, + 5 + ], + [ + 2, + 1, + 2, + 5 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 4, + 5, + 3, + 2, + 2 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 3 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 2, + 4, + 3, + 1 + ], + [ + 2, + 2, + 4, + 3, + 1 + ], + [ + 2, + 2, + 4, + 3, + 1 + ], + [ + 2, + 2, + 4, + 3, + 1 + ], + [ + 2, + 1, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 3, + 1 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 5, + 3 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 2, + 5 + ], + [ + 5, + 3, + 5, + 2, + 5 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4, + 5, + 4, + 1 + ], + [ + 3, + 4, + 5, + 4, + 1 + ], + [ + 3, + 4, + 5, + 4, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 5 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 3, + 3, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 2, + 2 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 3, + 1, + 5, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 4, + 3 + ], + [ + 4, + 1, + 1, + 4, + 3 + ], + [ + 4, + 1, + 1, + 4, + 3 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1, + 4, + 1 + ], + [ + 4, + 1, + 3, + 2, + 1 + ], + [ + 4, + 5, + 2, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 5, + 1, + 2, + 2 + ], + [ + 3, + 5, + 1, + 2, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3, + 2, + 4, + 3 + ], + [ + 1, + 3, + 2, + 4, + 3 + ], + [ + 1, + 3, + 2, + 4, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 3, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 4, + 3, + 5 + ], + [ + 4, + 1, + 4, + 5, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 2, + 2, + 4, + 1 + ], + [ + 1, + 2, + 2, + 4, + 1 + ], + [ + 1, + 2, + 2, + 4, + 1 + ], + [ + 1, + 2, + 2, + 4, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 2, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 4, + 5, + 5, + 2 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 1, + 4, + 5, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 2, + 1, + 4, + 3, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 3, + 4, + 2, + 4, + 4 + ], + [ + 3, + 4, + 2, + 4, + 4 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 4, + 3, + 5, + 5, + 2 + ], + [ + 4, + 1, + 1, + 3, + 5 + ], + [ + 4, + 1, + 1, + 3, + 5 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 3, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 5, + 4 + ], + [ + 3, + 1, + 4, + 5, + 1 + ], + [ + 3, + 1, + 4, + 5, + 1 + ], + [ + 3, + 1, + 4, + 5 + ], + [ + 3, + 1, + 4, + 5 + ], + [ + 3, + 1, + 4, + 5 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 4, + 5, + 3, + 4, + 2 + ], + [ + 4, + 5, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 2, + 5 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 5, + 2 + ], + [ + 3, + 2, + 1, + 5, + 2 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 3, + 2 + ], + [ + 4, + 4, + 3, + 3, + 2 + ], + [ + 4, + 4, + 3, + 3, + 2 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 4 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 3, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 1, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 4, + 5, + 5, + 5 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1, + 2 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 2, + 4, + 5, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 3, + 5, + 5, + 3 + ], + [ + 1, + 3, + 5, + 5, + 3 + ], + [ + 1, + 3, + 5, + 5, + 3 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 5, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 3, + 5, + 1, + 3, + 5 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 3, + 3, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 2, + 5 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 4, + 2, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 2, + 4 + ], + [ + 2, + 1, + 4, + 2, + 4 + ], + [ + 2, + 1, + 4, + 2, + 4 + ], + [ + 2, + 1, + 4, + 2, + 4 + ], + [ + 2, + 1, + 4, + 2, + 4 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 4 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 5, + 5, + 2, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 3, + 5 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 5 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 2, + 5, + 5, + 4 + ], + [ + 2, + 2, + 5, + 5, + 4 + ], + [ + 2, + 2, + 5, + 5, + 4 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 4 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 5, + 2 + ], + [ + 5, + 5, + 1, + 5, + 2 + ], + [ + 5, + 5, + 1, + 5, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 1, + 3, + 2, + 4 + ], + [ + 1, + 1, + 3, + 2, + 4 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 5, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 4, + 3, + 4, + 5 + ], + [ + 5, + 4, + 3, + 4, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 5, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 5, + 5, + 1, + 4, + 4 + ], + [ + 5, + 5, + 1, + 2, + 1 + ], + [ + 5, + 5, + 1, + 2, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 2, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 2, + 5, + 3 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 4, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 3, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 4, + 4, + 4 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 4, + 4, + 2, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 2, + 2 + ], + [ + 5, + 4, + 5, + 5, + 2 + ], + [ + 5, + 4, + 5, + 5, + 2 + ], + [ + 5, + 4, + 5, + 5, + 2 + ], + [ + 5, + 4, + 5, + 5, + 2 + ], + [ + 5, + 4, + 5, + 5, + 2 + ], + [ + 5 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 1, + 2, + 3 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 3, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4, + 2 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 5, + 1, + 3, + 4, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 2, + 5, + 2, + 4 + ], + [ + 5, + 2, + 5, + 2, + 4 + ], + [ + 5, + 2, + 5, + 2, + 4 + ], + [ + 5, + 2, + 5, + 2, + 4 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 4, + 5 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 3, + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4, + 4, + 2, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 1, + 2, + 3, + 2 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 3, + 2, + 1 + ], + [ + 4, + 5, + 3, + 2, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 5, + 3, + 3 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 4, + 2, + 5, + 4, + 2 + ], + [ + 4, + 2, + 5, + 4, + 2 + ], + [ + 4, + 2, + 5, + 4, + 2 + ], + [ + 4, + 2, + 5, + 4, + 2 + ], + [ + 4, + 2, + 1, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 4 + ], + [ + 4, + 2, + 1, + 1, + 4 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 5, + 5 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 2, + 5, + 2, + 5, + 2 + ], + [ + 4, + 1, + 2, + 2 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 2, + 5 + ], + [ + 2, + 1, + 2, + 2, + 5 + ], + [ + 2, + 1, + 2, + 2, + 5 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 2, + 4 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 1, + 3, + 3 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 2, + 4, + 2 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 4, + 5 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 5, + 4, + 5 + ], + [ + 5, + 3, + 4, + 3 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 4, + 5, + 2 + ], + [ + 4, + 4, + 5, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 3, + 4, + 5, + 1 + ], + [ + 5, + 3, + 4, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 5, + 3, + 2 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 4, + 1, + 3, + 3, + 5 + ], + [ + 4, + 1, + 3, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 3, + 4 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 4, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 2, + 2, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 4, + 3, + 5 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 2, + 1, + 3 + ], + [ + 1, + 3, + 2, + 1, + 3 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 5, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 5, + 3, + 4, + 3, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 2, + 4, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2, + 4, + 1, + 5 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 4 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 4, + 4, + 2 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 2, + 5, + 3 + ], + [ + 4, + 1, + 2, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 4, + 3 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 3, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 4, + 2, + 5, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 5, + 3 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 5, + 2, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 5, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 5, + 4 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 4, + 4, + 3, + 4, + 5 + ], + [ + 4, + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 1, + 3, + 1 + ], + [ + 4, + 3, + 1, + 3, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 3, + 2 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 4, + 5, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 3, + 5, + 3, + 3 + ], + [ + 5, + 3, + 5, + 3, + 3 + ], + [ + 5, + 3, + 5, + 3, + 3 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 5 + ], + [ + 4, + 5, + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 4, + 5, + 3 + ], + [ + 1, + 4, + 4, + 5, + 5 + ], + [ + 1, + 4, + 4, + 5, + 5 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 5, + 5, + 5, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 2, + 3, + 5 + ], + [ + 5, + 2, + 3, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 5, + 5, + 1, + 2 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 5, + 3, + 3, + 5, + 4 + ], + [ + 5, + 3, + 3, + 5, + 1 + ], + [ + 5, + 3, + 3, + 5, + 1 + ], + [ + 5, + 3, + 3, + 5, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 5, + 1, + 2 + ], + [ + 3, + 2, + 5, + 1, + 2 + ], + [ + 3, + 1, + 2, + 5, + 1 + ], + [ + 3, + 1, + 2, + 5, + 1 + ], + [ + 3, + 1, + 2, + 5, + 1 + ], + [ + 3, + 1, + 2, + 5, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 3, + 1, + 1, + 2, + 5 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 2, + 3, + 1, + 1, + 2 + ], + [ + 2, + 3, + 1, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2, + 1 + ], + [ + 2, + 3, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 5, + 4, + 4, + 5 + ], + [ + 3, + 5, + 4, + 4, + 1 + ], + [ + 3, + 5, + 4, + 4, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4, + 1 + ], + [ + 2, + 4, + 1, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 3, + 4, + 5, + 5, + 2 + ], + [ + 3, + 4, + 5, + 5, + 2 + ], + [ + 3, + 2, + 3, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 1, + 4, + 2, + 4 + ], + [ + 4, + 1, + 4, + 2, + 4 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 4, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 5, + 3, + 3 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 2, + 5, + 2 + ], + [ + 4, + 1, + 2, + 5, + 2 + ], + [ + 4, + 1, + 2, + 5, + 2 + ], + [ + 4, + 1, + 2, + 5, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4, + 2, + 5 + ], + [ + 2, + 4, + 2, + 5 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 5, + 2 + ], + [ + 3, + 2, + 4, + 5, + 2 + ], + [ + 3, + 2, + 4, + 5, + 2 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 2, + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 2 + ], + [ + 4, + 1, + 2, + 1, + 2 + ], + [ + 4, + 1, + 1, + 2, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1, + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 2, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 3, + 1, + 3, + 2, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 3, + 5, + 3, + 5, + 4 + ], + [ + 3, + 5, + 3, + 5, + 4 + ], + [ + 3, + 5, + 3, + 5, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 4, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 5, + 5, + 4, + 1, + 5 + ], + [ + 5, + 5, + 4, + 1, + 5 + ], + [ + 5, + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 5, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 2, + 3, + 2, + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 4, + 3 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4, + 4, + 5 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 3 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 5, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 1, + 5 + ], + [ + 1, + 3, + 2, + 3, + 2 + ], + [ + 1, + 3, + 2, + 3, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 5, + 3, + 4 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 4, + 4, + 2, + 4 + ], + [ + 3, + 4, + 4, + 2, + 4 + ], + [ + 3, + 4, + 4, + 2, + 4 + ], + [ + 3, + 4, + 4, + 2, + 4 + ], + [ + 3, + 1, + 1, + 5, + 5 + ], + [ + 3, + 1, + 1, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1, + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 3 + ], + [ + 4, + 3, + 2, + 4, + 1 + ], + [ + 4, + 3, + 2, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 2, + 5, + 5, + 3 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 2, + 4 + ], + [ + 2, + 3, + 2, + 4 + ], + [ + 2, + 3, + 2, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 2, + 3 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 4, + 3, + 3, + 2 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1, + 5, + 4, + 4, + 3 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 1, + 5, + 4, + 4, + 1 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 1, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 5 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 1, + 4, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 4, + 2, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 4, + 4 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 2, + 4, + 4 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 1, + 2 + ], + [ + 4, + 4, + 1, + 5 + ], + [ + 4, + 4, + 1, + 5 + ], + [ + 4, + 4, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 5 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 5, + 4, + 2, + 3, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 3, + 2, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 5, + 4 + ], + [ + 2, + 2, + 3, + 5, + 4 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 1, + 4, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 1, + 2, + 5, + 5 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 4, + 3, + 2 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 4, + 4, + 4, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 3, + 1, + 2, + 2 + ], + [ + 3, + 1, + 2, + 2 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 3, + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 4, + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 4, + 5, + 1 + ], + [ + 4, + 1, + 4, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 2, + 4, + 4 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 3 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 4, + 2, + 2, + 5 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 3, + 2 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 2, + 4, + 4 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 2, + 3 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1, + 1, + 3, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 4, + 3, + 5 + ], + [ + 1, + 1, + 4, + 3, + 5 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3, + 4, + 1, + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 4, + 4, + 2 + ], + [ + 2, + 4, + 4, + 1 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 2, + 1, + 4, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 3, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 1, + 4 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 5, + 1 + ], + [ + 4, + 5, + 4, + 5, + 1 + ], + [ + 4, + 5, + 4, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 5, + 3, + 4, + 2 + ], + [ + 3, + 5, + 3, + 4, + 2 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4, + 5, + 4 + ], + [ + 2, + 3, + 4, + 5, + 4 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 2, + 3, + 4, + 5, + 1 + ], + [ + 2, + 3, + 4, + 5, + 2 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4, + 2, + 3, + 3, + 1 + ], + [ + 4, + 2, + 3, + 3, + 1 + ], + [ + 4, + 2, + 3, + 3, + 1 + ], + [ + 4, + 2, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 5, + 3, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1, + 4, + 5, + 3 + ], + [ + 2, + 1, + 4, + 5, + 3 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 4, + 4 + ], + [ + 1, + 2, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1, + 5, + 5 + ], + [ + 5, + 4, + 1, + 5, + 1 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 5, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 5, + 5, + 3, + 3 + ], + [ + 3, + 5, + 5, + 3, + 3 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 2, + 3, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 4, + 5, + 4, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 3 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 2, + 2, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 4, + 5, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 1, + 4 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 2 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 4 + ], + [ + 1, + 2, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 2, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 4, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 3, + 3 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 2, + 2, + 5 + ], + [ + 4, + 2, + 2, + 5 + ], + [ + 4, + 2, + 2, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 2, + 2, + 2 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 4, + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 4, + 3 + ], + [ + 2, + 5, + 2, + 4, + 1 + ], + [ + 2, + 5, + 2, + 4, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 3, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 2, + 4, + 5, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 1, + 2, + 1 + ], + [ + 2, + 4, + 2, + 4, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 3, + 1, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 3, + 5, + 1, + 5 + ], + [ + 1, + 3, + 5, + 1, + 5 + ], + [ + 1, + 3, + 1, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 5, + 5, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 4, + 5, + 3 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 1, + 2, + 4 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2, + 2, + 2, + 4 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 5, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 5, + 4, + 5 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 5, + 2, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 5, + 5, + 1 + ], + [ + 3, + 2, + 5, + 5, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 3, + 3, + 2, + 2, + 4 + ], + [ + 1, + 1, + 2, + 1, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1, + 5 + ], + [ + 2, + 1, + 5, + 1, + 5 + ], + [ + 2, + 1, + 5, + 1, + 5 + ], + [ + 2, + 1, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 1, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 3 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 5, + 2, + 4, + 5, + 5 + ], + [ + 5, + 2, + 4, + 5, + 5 + ], + [ + 5, + 2, + 4, + 5, + 5 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 2, + 4, + 5 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1, + 4 + ], + [ + 5, + 2, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 2, + 4 + ], + [ + 5, + 4, + 2, + 4 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 4, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2, + 2, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 3, + 1, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 3, + 3, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 5, + 1, + 5 + ], + [ + 1, + 1, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 2, + 1 + ], + [ + 5, + 5, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 4, + 5, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 5, + 3, + 5, + 5, + 5 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 5 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 4, + 2, + 2 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 5, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 4 + ], + [ + 5, + 1, + 5, + 4 + ], + [ + 5, + 1, + 5, + 4 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 2, + 3 + ], + [ + 1, + 1, + 2, + 3 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 2, + 2, + 3, + 4 + ], + [ + 1, + 2, + 2, + 3, + 4 + ], + [ + 1, + 2, + 2, + 3, + 4 + ], + [ + 1, + 2, + 2, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 4, + 4, + 5, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5, + 1, + 2, + 5 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 4, + 3, + 4 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 3, + 1 + ], + [ + 1, + 3, + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 3, + 2, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 5, + 1 + ], + [ + 3, + 3, + 5, + 1, + 5 + ], + [ + 3, + 3, + 5, + 1, + 5 + ], + [ + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 2, + 3, + 5, + 5, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 5 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 3, + 5, + 4 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 1, + 3, + 4, + 1 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 4, + 3 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2, + 1, + 5, + 4, + 3 + ], + [ + 2, + 1, + 5, + 4, + 3 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 2, + 1, + 5, + 4, + 2 + ], + [ + 2, + 1, + 5, + 1, + 4 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 3, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 1, + 4, + 1 + ], + [ + 1, + 5, + 1, + 4, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 2, + 5, + 3, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 4, + 5, + 2 + ], + [ + 1, + 4, + 4, + 5, + 2 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 5, + 5, + 4 + ], + [ + 4, + 1, + 1, + 5, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 5, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 3, + 4, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1, + 3, + 1, + 4 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 3 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 2, + 3, + 1, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1, + 4 + ], + [ + 2, + 3, + 1, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 1, + 5, + 1, + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 2, + 4, + 4 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 2, + 2 + ], + [ + 2, + 5, + 1, + 2, + 2 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 4, + 2 + ], + [ + 3, + 3, + 1, + 4, + 2 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1, + 3 + ], + [ + 5, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 3, + 4, + 4 + ], + [ + 3, + 4, + 3, + 4, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 3, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 2, + 4, + 5 + ], + [ + 2, + 3, + 2, + 4, + 5 + ], + [ + 2, + 3, + 2, + 4, + 5 + ], + [ + 2, + 3, + 2, + 4, + 5 + ], + [ + 2, + 1, + 5, + 3, + 5 + ], + [ + 2, + 1, + 5, + 3, + 5 + ], + [ + 2, + 1, + 5, + 3, + 5 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 1, + 5, + 4, + 3 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 2, + 1, + 1, + 4 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 1, + 2, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 2, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 5, + 4 + ], + [ + 4, + 2, + 1, + 5, + 4 + ], + [ + 4, + 2, + 1, + 1, + 3 + ], + [ + 4, + 2, + 2, + 5, + 1 + ], + [ + 4, + 5, + 2, + 5, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 1, + 3, + 2, + 1 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 4, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 1, + 3, + 5 + ], + [ + 1, + 4, + 3, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 2, + 3 + ], + [ + 3, + 3, + 2, + 3 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 3, + 3, + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 5, + 2 + ], + [ + 2, + 3, + 5, + 2 + ], + [ + 2, + 3, + 5, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 1, + 1, + 1, + 2, + 4 + ], + [ + 1, + 1, + 1, + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 3, + 4, + 4 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 3, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 3, + 4, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 4, + 5, + 3, + 2, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 5, + 4, + 3 + ], + [ + 4, + 4, + 5, + 4, + 3 + ], + [ + 4, + 4, + 5, + 4, + 3 + ], + [ + 4, + 4, + 5, + 4, + 3 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 4, + 5, + 2 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4, + 4, + 3, + 3 + ], + [ + 2, + 4, + 4, + 3, + 3 + ], + [ + 2, + 4, + 4, + 3, + 3 + ], + [ + 2, + 4, + 4, + 3, + 3 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2, + 3, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 2, + 1, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 3 + ], + [ + 4, + 4, + 3, + 1, + 3 + ], + [ + 4, + 4, + 1, + 4, + 1 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 4, + 3, + 3, + 4, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 1, + 5, + 5, + 2, + 3 + ], + [ + 1, + 5, + 5, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 3 + ], + [ + 5, + 3, + 1, + 3 + ], + [ + 5, + 3, + 1, + 3 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 4, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 5, + 5, + 2 + ], + [ + 1, + 5, + 5, + 5, + 1 + ], + [ + 1, + 5, + 5, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 5, + 5 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 3, + 2, + 1, + 2, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 3, + 5, + 4 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 1 + ], + [ + 2, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 2 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 1, + 5, + 1 + ], + [ + 2, + 4, + 4, + 2, + 3 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 2, + 5 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 3 + ], + [ + 3, + 2, + 3, + 1, + 2 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 4, + 5, + 3, + 3 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 3, + 5, + 5 + ], + [ + 1, + 3, + 3, + 5, + 5 + ], + [ + 1, + 3, + 3, + 5, + 5 + ], + [ + 1, + 3, + 3, + 5, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 4, + 4, + 5, + 4 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 3, + 5, + 1, + 1 + ], + [ + 2, + 3, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 4, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 4, + 5, + 2, + 3 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 5, + 1, + 2, + 5 + ], + [ + 2, + 5, + 1, + 2, + 5 + ], + [ + 2, + 5, + 1, + 4, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 2, + 5, + 4, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 3, + 5, + 3, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 3, + 2, + 4, + 4 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5, + 4, + 3 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 2, + 2 + ], + [ + 5, + 1, + 5, + 2, + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3, + 3 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 1, + 3, + 4, + 5, + 5 + ], + [ + 1, + 3, + 4, + 5, + 1 + ], + [ + 1, + 3, + 4, + 5, + 1 + ], + [ + 1, + 3, + 4, + 5, + 1 + ], + [ + 1, + 3, + 4, + 5, + 1 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 1, + 3, + 1, + 1, + 5 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1, + 2 + ], + [ + 4, + 2, + 3, + 4, + 1 + ], + [ + 4, + 2, + 3, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 4, + 5, + 5, + 4 + ], + [ + 5, + 4, + 5, + 5, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 5, + 3, + 3 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 3 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 3, + 5, + 4 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 5 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 2, + 5, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 4, + 2 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 4, + 3, + 4 + ], + [ + 5, + 2, + 4, + 3, + 4 + ], + [ + 5, + 2, + 4, + 3, + 4 + ], + [ + 5, + 2, + 4, + 3, + 4 + ], + [ + 5, + 2, + 4, + 3, + 4 + ], + [ + 5, + 2, + 4, + 5, + 4 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 1, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 4 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 4 + ], + [ + 3, + 1, + 2, + 3, + 3 + ], + [ + 3, + 1, + 2, + 3, + 3 + ], + [ + 3, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 5, + 5, + 1, + 3 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 2, + 5 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 4, + 5, + 4, + 1 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 5, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 5, + 5, + 5 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 4, + 5, + 5 + ], + [ + 4, + 3, + 4, + 5, + 5 + ], + [ + 4, + 3, + 4, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 3, + 1, + 5, + 5 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 4, + 5, + 5, + 4 + ], + [ + 3, + 4, + 5, + 5, + 4 + ], + [ + 3, + 4, + 5, + 5, + 4 + ], + [ + 3, + 4, + 5, + 5, + 4 + ], + [ + 3, + 4, + 5, + 5, + 4 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 4, + 5, + 5, + 3, + 5 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 2, + 1, + 4, + 3 + ], + [ + 4, + 2, + 1, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1, + 5 + ], + [ + 4, + 2, + 5, + 1, + 5 + ], + [ + 4 + ], + [ + 5, + 4, + 5, + 1, + 4 + ], + [ + 5, + 4, + 5, + 1, + 4 + ], + [ + 5, + 4, + 5, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4, + 1, + 2 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 3, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 2, + 1, + 5, + 5 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 2, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 3, + 2, + 4 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 2, + 3, + 1, + 4 + ], + [ + 3, + 2, + 3, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 3, + 2, + 3, + 3 + ], + [ + 3, + 1, + 1, + 2, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 4, + 4, + 5 + ], + [ + 3, + 2, + 4, + 1, + 2 + ], + [ + 3, + 2, + 4, + 1, + 2 + ], + [ + 3, + 2, + 4, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 1, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 2, + 3, + 5, + 4, + 3 + ], + [ + 2, + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 1, + 4, + 5 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 3, + 4, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 5, + 1 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 1, + 4, + 2 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 2, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1, + 2 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 4, + 3, + 5 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 2, + 3, + 1, + 3, + 5 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 4, + 5, + 1, + 4 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 3, + 4, + 1, + 4, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 2, + 2, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 2 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 3, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 2, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 4, + 5 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 2 + ], + [ + 2, + 4, + 2, + 1, + 2 + ], + [ + 2, + 4, + 2, + 1, + 2 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 5, + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 4 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 4, + 1, + 5, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 3, + 5, + 3 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 4, + 5, + 4, + 3 + ], + [ + 2, + 4, + 5, + 4, + 3 + ], + [ + 2, + 4, + 5, + 4, + 3 + ], + [ + 2, + 4, + 5, + 4, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 4 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 2, + 3, + 1, + 4 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 4, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2, + 5, + 5, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 2, + 5 + ], + [ + 5, + 4, + 2, + 5 + ], + [ + 5, + 4, + 2, + 1 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 5, + 4, + 2, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 1, + 2, + 4, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 3, + 4, + 5, + 2 + ], + [ + 4, + 3, + 4, + 5, + 2 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 5, + 1, + 2 + ], + [ + 4, + 3, + 5, + 1, + 2 + ], + [ + 4, + 3, + 5, + 1, + 2 + ], + [ + 4, + 3, + 5, + 1, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 5, + 4 + ], + [ + 3, + 1, + 5, + 4 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 5, + 4, + 4, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3 + ], + [ + 4, + 4, + 5, + 2 + ], + [ + 4, + 4, + 5, + 2 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 3, + 4, + 3, + 2, + 1 + ], + [ + 3, + 4, + 3, + 2, + 1 + ], + [ + 3, + 4, + 3, + 2, + 1 + ], + [ + 3, + 4, + 3, + 2, + 1 + ], + [ + 3, + 4, + 3, + 2, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 3, + 1, + 1 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 5, + 2, + 2, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 1, + 3, + 4, + 4, + 3 + ], + [ + 1, + 3, + 4, + 4, + 3 + ], + [ + 1, + 3, + 4, + 4, + 3 + ], + [ + 1, + 3, + 4, + 4, + 3 + ], + [ + 1, + 3, + 4, + 1, + 4 + ], + [ + 1, + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 5, + 2 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5, + 5 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 1, + 1, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 2, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 3, + 5 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 4, + 2, + 1 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 5, + 5, + 3, + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 3, + 5, + 3, + 4, + 4 + ], + [ + 3, + 5, + 3, + 4, + 4 + ], + [ + 3, + 5, + 1, + 1, + 3 + ], + [ + 3, + 5, + 1, + 1, + 3 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 4, + 2 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 2, + 3 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 5, + 4 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 3, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2, + 2 + ], + [ + 4, + 2, + 2 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 1, + 1 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 1, + 2, + 4, + 5, + 1 + ], + [ + 1, + 1, + 2, + 4, + 5 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 1 + ], + [ + 1, + 2, + 1, + 3, + 4 + ], + [ + 1, + 2, + 1, + 3, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 5, + 5, + 5, + 2 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 5, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 5, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 2 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 2, + 2 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 5, + 4, + 4, + 5, + 4 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5, + 1, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 5, + 2, + 4, + 4 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 4, + 3, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 4, + 5, + 5, + 2, + 1 + ], + [ + 4, + 5, + 5, + 2, + 1 + ], + [ + 4, + 5, + 5, + 2, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 4 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 1, + 3, + 1, + 5 + ], + [ + 1, + 1, + 3, + 1, + 5 + ], + [ + 1, + 1, + 3, + 1, + 5 + ], + [ + 1, + 1, + 3, + 1, + 5 + ], + [ + 1, + 1, + 3, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1, + 3, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 3 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 5, + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 1, + 2, + 3, + 5 + ], + [ + 4, + 1, + 2, + 3, + 5 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1, + 3 + ], + [ + 1, + 3, + 1, + 1, + 3 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1, + 4, + 5, + 1 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 1, + 4, + 1, + 4 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 5 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 4, + 5, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 4 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 4, + 5, + 5 + ], + [ + 3, + 1, + 4, + 5 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 2, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 3, + 2, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2, + 2 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3, + 3, + 2, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 2, + 5, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 3, + 1 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3, + 3 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 3, + 5, + 5 + ], + [ + 2, + 2, + 3, + 5, + 5 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 2, + 3 + ], + [ + 5, + 1, + 1, + 2, + 3 + ], + [ + 5, + 1, + 1, + 2, + 3 + ], + [ + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1 + ], + [ + 1, + 1, + 5, + 1, + 4 + ], + [ + 1, + 1, + 5, + 1, + 4 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 4, + 5, + 4, + 5, + 3 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 5 + ], + [ + 2, + 5, + 3, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 5, + 4, + 2 + ], + [ + 1, + 4, + 2, + 2, + 1 + ], + [ + 1, + 4, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3, + 3 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 2, + 1, + 1 + ], + [ + 1, + 3, + 1, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 4, + 2, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 1, + 3, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 5, + 3, + 1, + 5 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 4, + 3, + 5, + 4 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 3, + 2, + 3, + 2, + 2 + ], + [ + 3, + 2, + 3, + 2, + 2 + ], + [ + 3, + 2, + 3, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 4 + ], + [ + 3, + 2, + 4, + 1, + 4 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1, + 1 + ], + [ + 3, + 4, + 2, + 5 + ], + [ + 3, + 4, + 2, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 5, + 4, + 2, + 4 + ], + [ + 1, + 5, + 4, + 1, + 1 + ], + [ + 1, + 5, + 4, + 1, + 1 + ], + [ + 1, + 5, + 4, + 1, + 1 + ], + [ + 1, + 5, + 4, + 2, + 2 + ], + [ + 1, + 5, + 4, + 2, + 2 + ], + [ + 1, + 3, + 3, + 5, + 4 + ], + [ + 5, + 4, + 5, + 1, + 3 + ], + [ + 5, + 4, + 5, + 1, + 3 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 5 + ], + [ + 5, + 1, + 4, + 2 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2, + 5, + 1, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 2, + 4, + 3, + 4 + ], + [ + 2, + 4, + 3, + 4 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 2, + 4 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 1 + ], + [ + 2, + 2, + 2, + 5 + ], + [ + 2, + 2, + 2, + 5 + ], + [ + 2, + 2, + 2, + 5 + ], + [ + 2, + 2, + 2, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 5, + 1, + 2 + ], + [ + 2, + 3, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 2, + 3 + ], + [ + 2, + 5, + 5, + 1, + 3 + ], + [ + 2, + 5, + 2, + 1, + 3 + ], + [ + 2, + 5, + 5, + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 5 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 4, + 2, + 2, + 4 + ], + [ + 4, + 2, + 2, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 1, + 5, + 5 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 2, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 5, + 5, + 5 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 5, + 2, + 5 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 5, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 4, + 2, + 2 + ], + [ + 2, + 2, + 4, + 2, + 2 + ], + [ + 2, + 2, + 4, + 2, + 2 + ], + [ + 2, + 2, + 4, + 2, + 2 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 2, + 3, + 5, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3, + 5, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 1, + 2, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 5, + 5, + 5 + ], + [ + 4, + 3, + 5, + 5, + 5 + ], + [ + 4, + 3, + 5, + 1, + 5 + ], + [ + 4, + 3, + 5, + 1, + 5 + ], + [ + 4, + 3, + 5, + 1, + 5 + ], + [ + 4, + 3, + 5, + 1, + 1 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 2, + 5 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 3, + 4, + 2 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 5, + 2 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 1, + 1, + 5, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1, + 3 + ], + [ + 5, + 1, + 4, + 1, + 3 + ], + [ + 5, + 1, + 4, + 1, + 3 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 2, + 5, + 3, + 3, + 4 + ], + [ + 2, + 5, + 3, + 3, + 4 + ], + [ + 2, + 5, + 3, + 3, + 4 + ], + [ + 2, + 5, + 3, + 3, + 4 + ], + [ + 2, + 5, + 3, + 3, + 4 + ], + [ + 2, + 5, + 3, + 3, + 1 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 3, + 1, + 1 + ], + [ + 1, + 3, + 3, + 1, + 3 + ], + [ + 1, + 3, + 3, + 1, + 3 + ], + [ + 1, + 3, + 3, + 5, + 1 + ], + [ + 1, + 3, + 5, + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1, + 2 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 4, + 3, + 3 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 1, + 1, + 3, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 4 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 1, + 5, + 2, + 1 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 3 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 2, + 2, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 5, + 5, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 2 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 2, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 4, + 2 + ], + [ + 5, + 3, + 1, + 4, + 2 + ], + [ + 1, + 4, + 2, + 1, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 2 + ], + [ + 5, + 1, + 1, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 5 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 3, + 4, + 4, + 3, + 1 + ], + [ + 2, + 5, + 2, + 4, + 2 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 2, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 2, + 3, + 1 + ], + [ + 1, + 4, + 2, + 3, + 1 + ], + [ + 1, + 4, + 2, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 3, + 2 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 3, + 3, + 2, + 3, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 2, + 3, + 3, + 2, + 2 + ], + [ + 1, + 1, + 4, + 3, + 3 + ], + [ + 1, + 1, + 4, + 3, + 3 + ], + [ + 1, + 1, + 4, + 3, + 3 + ], + [ + 1, + 1, + 4, + 3, + 3 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 2, + 5 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 2, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 2, + 2, + 3, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 1 + ], + [ + 3, + 4, + 3, + 4 + ], + [ + 3, + 4, + 3, + 4 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 2, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 4, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 2, + 1, + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 2, + 2, + 3, + 4, + 4 + ], + [ + 2, + 2, + 3, + 4, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 5, + 2 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 5 + ], + [ + 1, + 5, + 1, + 1, + 5 + ], + [ + 1, + 5, + 1, + 1, + 5 + ], + [ + 1, + 5, + 1, + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 3, + 4, + 2, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 4, + 2 + ], + [ + 3, + 2, + 1, + 5, + 3 + ], + [ + 3, + 2, + 1, + 5, + 3 + ], + [ + 3, + 2, + 1, + 5, + 3 + ], + [ + 3, + 2, + 1, + 1, + 4 + ], + [ + 4, + 4, + 2, + 3, + 2 + ], + [ + 4, + 4, + 2, + 3, + 2 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 4, + 1 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 4 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 1, + 4, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 1, + 2, + 2 + ], + [ + 1, + 2, + 2 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 4, + 3, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 3, + 1, + 2, + 2, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 4, + 1, + 3, + 5 + ], + [ + 4, + 4, + 1, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 3, + 2, + 2, + 3, + 4 + ], + [ + 3, + 2, + 2, + 3, + 4 + ], + [ + 3, + 2, + 2, + 3, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 3, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 3 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4 + ], + [ + 2, + 4, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 4, + 5 + ], + [ + 1, + 1, + 4, + 5 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 5, + 2, + 1, + 5 + ], + [ + 3, + 3, + 3 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 4, + 1, + 4, + 1 + ], + [ + 4, + 4, + 1, + 4, + 1 + ], + [ + 4, + 4, + 1, + 4, + 3 + ], + [ + 4, + 4, + 1, + 4, + 3 + ], + [ + 4, + 4, + 1, + 4, + 3 + ], + [ + 4, + 4, + 1, + 4, + 3 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 5, + 2, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 5, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 3, + 5, + 4, + 2 + ], + [ + 4, + 3, + 5, + 4, + 2 + ], + [ + 4, + 3, + 5, + 4, + 2 + ], + [ + 4, + 3, + 5, + 4, + 2 + ], + [ + 4, + 3, + 5, + 2, + 1 + ], + [ + 4, + 3, + 5, + 4, + 1 + ], + [ + 4, + 3, + 5, + 4, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 3 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4, + 2, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 2 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 2, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 5, + 3, + 1, + 4, + 2 + ], + [ + 5, + 3, + 1, + 4, + 2 + ], + [ + 5, + 3, + 1, + 4, + 2 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 3, + 1, + 1, + 2 + ], + [ + 5, + 2, + 4, + 1, + 3 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 3, + 3, + 4, + 3, + 1 + ], + [ + 3, + 3, + 4, + 3, + 1 + ], + [ + 3, + 3, + 4, + 1, + 1 + ], + [ + 3, + 1, + 3, + 4, + 1 + ], + [ + 3, + 1, + 3, + 4, + 1 + ], + [ + 3, + 1, + 3, + 4, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 3, + 4, + 5, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 4, + 2, + 5, + 4 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5, + 5, + 4, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3, + 5, + 5, + 1, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 2, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 1, + 5, + 5, + 3 + ], + [ + 1, + 1, + 5, + 5, + 3 + ], + [ + 1, + 1, + 5, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1, + 1 + ], + [ + 5, + 3, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 5, + 5, + 2 + ], + [ + 5, + 2, + 1, + 3, + 2 + ], + [ + 5, + 2, + 1, + 3, + 2 + ], + [ + 5, + 2, + 1, + 3, + 2 + ], + [ + 5, + 2, + 1, + 3, + 2 + ], + [ + 5, + 2, + 1, + 3, + 2 + ], + [ + 5, + 2, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 3, + 3, + 4, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 5, + 2, + 2 + ], + [ + 1, + 5, + 2, + 2 + ], + [ + 1, + 5, + 2, + 2 + ], + [ + 1, + 5, + 2, + 2 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 1, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 5, + 1, + 4 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 2, + 2, + 5 + ], + [ + 1, + 4, + 2, + 2, + 5 + ], + [ + 1, + 3, + 1, + 3, + 1 + ], + [ + 1, + 3, + 1, + 1, + 1 + ], + [ + 1, + 3, + 1, + 3, + 1 + ], + [ + 3 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 4, + 4, + 4, + 5 + ], + [ + 1, + 4, + 4, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 4, + 3 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 2, + 3, + 1 + ], + [ + 5, + 1, + 2, + 3, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 4, + 2, + 4 + ], + [ + 5, + 1, + 4, + 2, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 2, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 3, + 1, + 5 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1 + ], + [ + 4, + 5, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 3, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 2, + 5, + 4, + 5, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 2, + 5 + ], + [ + 2, + 4, + 3, + 5 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 1, + 1 + ], + [ + 2, + 4, + 5, + 3, + 1 + ], + [ + 2, + 4, + 5, + 3, + 1 + ], + [ + 2, + 4, + 5, + 3, + 2 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 3, + 4, + 1, + 4 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 5 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 5, + 1 + ], + [ + 4, + 1, + 3, + 5, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 4, + 3, + 2 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 4, + 5, + 4, + 3, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 3, + 2, + 3 + ], + [ + 2, + 4, + 3, + 2, + 3 + ], + [ + 2, + 1, + 1, + 5, + 5 + ], + [ + 2, + 1, + 1, + 5, + 5 + ], + [ + 2, + 1, + 1, + 5, + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 3, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 3, + 1, + 1, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 4, + 3, + 5, + 3 + ], + [ + 4, + 3, + 2, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4, + 4, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 3, + 3, + 4, + 1, + 5 + ], + [ + 3, + 3, + 4, + 1, + 5 + ], + [ + 3, + 3, + 4, + 1, + 5 + ], + [ + 3, + 3, + 4, + 1, + 5 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 5, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4, + 4, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 5, + 5, + 4, + 5 + ], + [ + 4, + 5, + 5, + 4, + 5 + ], + [ + 4, + 5, + 5, + 4, + 5 + ], + [ + 4, + 5, + 5, + 4, + 5 + ], + [ + 4, + 5, + 5, + 4, + 5 + ], + [ + 4, + 5, + 5, + 4, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 3, + 3, + 4 + ], + [ + 1, + 2, + 3, + 2, + 4 + ], + [ + 1, + 2, + 3, + 2, + 4 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 3, + 4, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 2, + 1, + 4, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 3, + 5 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4, + 5, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 5, + 3 + ], + [ + 2, + 3, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 2, + 2 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 2, + 4 + ], + [ + 3, + 2, + 1, + 2, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 4, + 4 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 4, + 5, + 5 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 4, + 1, + 2, + 3, + 5 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 3, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 4, + 1, + 2, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 1, + 1, + 2, + 1, + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 1, + 2, + 1, + 2 + ], + [ + 2, + 1, + 2, + 1, + 2 + ], + [ + 2, + 1, + 2, + 1, + 2 + ], + [ + 2, + 1, + 2, + 1, + 2 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 2, + 1, + 2, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 1, + 2, + 2 + ], + [ + 3, + 1, + 3, + 5, + 1 + ], + [ + 3, + 1, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 3, + 1, + 5, + 3, + 5 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 4, + 4, + 3, + 2, + 4 + ], + [ + 4, + 4, + 3, + 2, + 4 + ], + [ + 4, + 4, + 2, + 1, + 3 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 4 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 3, + 5 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 4, + 5, + 1 + ], + [ + 2, + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 2, + 3, + 1 + ], + [ + 2, + 2, + 2, + 3, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 4 + ], + [ + 1, + 4 + ], + [ + 3, + 3, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 3, + 1, + 2 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 1, + 2, + 4 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 2, + 5, + 5 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 2 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 2 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 2, + 5, + 2 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 4, + 2, + 4, + 3, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4, + 4, + 3 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 1, + 5, + 1, + 5, + 1 + ], + [ + 3, + 1, + 2, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 3, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 2, + 2, + 4 + ], + [ + 3, + 2, + 2, + 3 + ], + [ + 3, + 2, + 2, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 3, + 2, + 3, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3, + 3 + ], + [ + 3, + 2, + 3, + 3, + 3 + ], + [ + 3, + 2, + 1, + 4, + 3 + ], + [ + 3, + 2, + 1, + 4, + 3 + ], + [ + 3, + 2, + 1, + 4, + 3 + ], + [ + 1, + 5, + 3, + 5, + 1 + ], + [ + 1, + 5, + 3, + 5, + 1 + ], + [ + 1, + 5, + 3, + 5, + 1 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 1, + 2, + 1, + 3 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 5, + 5, + 3 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 3, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 1, + 3, + 1, + 1 + ], + [ + 5, + 5, + 2 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 2, + 5 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 3, + 5, + 2 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 5, + 1, + 5 + ], + [ + 1, + 5, + 1, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 4 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 2 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 4, + 2 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 1, + 2, + 3 + ], + [ + 5, + 2, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 3, + 2, + 1 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 4 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 5, + 5, + 5, + 5 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 5, + 4, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4, + 4 + ], + [ + 1, + 5, + 4, + 4, + 4 + ], + [ + 1, + 4, + 1, + 3, + 3 + ], + [ + 1, + 4, + 1, + 3, + 3 + ], + [ + 1, + 4, + 1, + 3, + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 3 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2, + 3, + 4, + 1 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 2, + 2, + 1, + 3 + ], + [ + 5, + 2, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 3, + 4, + 3, + 2 + ], + [ + 4, + 3, + 4, + 3, + 2 + ], + [ + 4, + 3, + 4, + 3, + 2 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1, + 3, + 4, + 2 + ], + [ + 2, + 1, + 3, + 4, + 2 + ], + [ + 2, + 1, + 3, + 4, + 2 + ], + [ + 2, + 1, + 3, + 4, + 2 + ], + [ + 2, + 1, + 3, + 4, + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 2, + 2 + ], + [ + 5, + 2, + 2, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 4, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 4, + 2 + ], + [ + 1, + 4, + 4, + 1, + 1 + ], + [ + 1, + 4, + 4, + 4, + 1 + ], + [ + 1, + 2, + 1, + 4, + 1 + ], + [ + 5, + 5, + 3, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4, + 5, + 5, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 4, + 4, + 5 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4, + 3 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 2 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 2, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1, + 3 + ], + [ + 5, + 3, + 3, + 2, + 1 + ], + [ + 5, + 3, + 3, + 3, + 1 + ], + [ + 5, + 3, + 3, + 3, + 1 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 3 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 3, + 3, + 2, + 3 + ], + [ + 1, + 3, + 3, + 2, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 2, + 5, + 3 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 4, + 4, + 2 + ], + [ + 2, + 4, + 4, + 2 + ], + [ + 2, + 4, + 4, + 2 + ], + [ + 2, + 4, + 4, + 2 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 1, + 2, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 4 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 2 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 3, + 5, + 3 + ], + [ + 1, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3, + 2, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4, + 3, + 5, + 1 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 4, + 4, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 3, + 1, + 4, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 1, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 4, + 5, + 1, + 2 + ], + [ + 3, + 4, + 5, + 1, + 2 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5, + 2, + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 5, + 2 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 4, + 3 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 4, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 2 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 3, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 4 + ], + [ + 5, + 1 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 3, + 4, + 4, + 3 + ], + [ + 4, + 4, + 1, + 3, + 1 + ], + [ + 4, + 4, + 1, + 3, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 3, + 1, + 3 + ], + [ + 2, + 5, + 3, + 1, + 3 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 4, + 5, + 3, + 5 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 4, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 2, + 1, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 5, + 3 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 5 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 1, + 5, + 5 + ], + [ + 5, + 1, + 5, + 5 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4, + 4, + 4, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 3, + 4, + 5 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 2, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 3, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 3, + 4, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5, + 3, + 2, + 3, + 5 + ], + [ + 5, + 3, + 2, + 3, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1, + 1 + ], + [ + 4, + 4, + 4, + 3, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 3, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 2, + 1, + 2 + ], + [ + 1 + ], + [ + 2, + 2, + 5, + 2, + 5 + ], + [ + 2, + 2, + 5, + 2, + 5 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 4 + ], + [ + 1, + 3, + 5, + 1, + 3 + ], + [ + 1, + 3, + 5, + 1, + 3 + ], + [ + 1, + 3, + 5, + 1, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 3, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 2, + 5, + 5, + 4, + 3 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 4, + 3, + 4 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 3, + 5, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 5, + 1, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 5, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1 + ], + [ + 1, + 2, + 4, + 5, + 2 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2, + 4, + 1, + 5 + ], + [ + 2, + 1, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 3, + 3, + 5 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1 + ], + [ + 2, + 2, + 2, + 1, + 5 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 5, + 1 + ], + [ + 5, + 1, + 5, + 5, + 1 + ], + [ + 5, + 1, + 5, + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 4, + 1, + 3, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 2, + 1, + 3, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 5, + 5, + 5, + 3 + ], + [ + 4, + 5, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 2, + 1 + ], + [ + 1, + 4, + 1, + 3 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5, + 2, + 2, + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 2, + 3 + ], + [ + 1, + 1, + 1, + 2, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 3, + 3, + 2, + 4 + ], + [ + 5, + 3, + 3, + 2, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 1, + 1, + 5, + 1 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 3, + 5, + 2, + 3, + 4 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 5, + 2 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 4, + 2, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 1 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 2, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 4, + 2, + 4 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 4, + 3, + 4 + ], + [ + 3, + 5, + 1, + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 5 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 4, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 1, + 4, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 3, + 3, + 3, + 5, + 4 + ], + [ + 3, + 3, + 3, + 5, + 4 + ], + [ + 3, + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 3, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1, + 4, + 5, + 3 + ], + [ + 5, + 1, + 4, + 5, + 3 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 5 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 1 + ], + [ + 5, + 4, + 5, + 3, + 2 + ], + [ + 5, + 4, + 5, + 3, + 2 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 2 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 3, + 5, + 1 + ], + [ + 1, + 3, + 5 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 3, + 1, + 3 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 5 + ], + [ + 1, + 4, + 5, + 3, + 4 + ], + [ + 1, + 4, + 5, + 3, + 4 + ], + [ + 1, + 4, + 5, + 3, + 4 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1, + 4, + 1, + 5, + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 1, + 2 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 2, + 4, + 2, + 5, + 4 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 4, + 4, + 4, + 1 + ], + [ + 2, + 5, + 5, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2, + 4, + 4 + ], + [ + 3, + 2, + 4, + 1 + ], + [ + 3, + 2, + 3, + 5 + ], + [ + 3, + 1, + 3, + 5 + ], + [ + 3, + 1, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 3, + 3, + 1, + 5 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 5, + 3, + 1, + 1, + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 2, + 5, + 3 + ], + [ + 2, + 2, + 5, + 3 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 5, + 1, + 5, + 1 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 2, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 1, + 3, + 2, + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 2, + 4, + 2 + ], + [ + 4, + 2, + 4, + 1 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 4, + 2, + 3, + 2 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 4, + 1, + 5 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 5, + 4 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 2, + 1, + 3, + 5 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 4, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 1, + 2, + 2, + 5 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 4, + 5, + 4, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 3, + 1 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 2, + 5 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 2, + 2, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 2, + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 2 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 1, + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 4, + 3 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 2 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 3, + 1, + 3 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 3, + 3, + 5 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 3, + 3, + 3, + 1 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 4 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 2 + ], + [ + 1, + 5, + 1 + ], + [ + 1, + 5, + 1 + ], + [ + 2, + 5, + 4, + 2, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 5, + 4, + 4, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 3, + 1, + 1, + 4 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 1 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 3, + 4 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 1, + 1, + 4, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 1, + 1, + 4 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 4, + 1 + ], + [ + 5, + 1, + 4, + 4, + 1 + ], + [ + 5, + 1, + 4, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 4, + 1 + ], + [ + 5, + 1, + 2, + 2, + 4 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 1 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1, + 2, + 1, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 5, + 5, + 3, + 2 + ], + [ + 3, + 5, + 5, + 1, + 1 + ], + [ + 3, + 5, + 4, + 5, + 4 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 3, + 2, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5, + 4, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 5, + 2, + 5, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 5 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 1, + 5, + 1, + 3, + 3 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 2, + 2, + 5, + 3, + 1 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 1, + 5 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 4, + 3, + 1 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1, + 3, + 3, + 4 + ], + [ + 1 + ], + [ + 2, + 3, + 3, + 5, + 1 + ], + [ + 2, + 3, + 3, + 5, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2, + 3, + 3, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4, + 3, + 2, + 4 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 3, + 1, + 1, + 3 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 4 + ], + [ + 4, + 1, + 4, + 3 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 3 + ], + [ + 4, + 3, + 3 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 4 + ], + [ + 3, + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 2, + 2, + 3, + 2 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 1, + 2, + 2, + 1, + 1 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 3 + ], + [ + 2, + 3, + 3, + 2 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 2, + 3, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 4, + 5 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 5, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 5, + 3 + ], + [ + 5, + 3 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 5, + 3, + 1, + 4 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 4 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 5, + 5, + 4 + ], + [ + 2, + 4, + 5, + 5, + 4 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 5, + 5, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5, + 5 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 4 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 5 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 1, + 3, + 4, + 1 + ], + [ + 2, + 1, + 3, + 4, + 1 + ], + [ + 2, + 1, + 3, + 1, + 1 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 2, + 1, + 3, + 1, + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 2 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 1, + 5, + 1, + 2, + 1 + ], + [ + 1, + 2, + 1, + 2, + 1 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 1, + 2 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 4, + 3, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 4 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 2 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 3, + 1, + 1, + 4, + 1 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 5, + 1, + 1, + 5 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 5 + ], + [ + 2 + ], + [ + 3, + 1, + 3, + 4, + 5 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 4 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 3, + 5 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5, + 4, + 1, + 2, + 2 + ], + [ + 5, + 4, + 1, + 2, + 2 + ], + [ + 5, + 4, + 1, + 2, + 2 + ], + [ + 5, + 4, + 1, + 2, + 2 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 4, + 1, + 3, + 1 + ], + [ + 5, + 1, + 3, + 2, + 1 + ], + [ + 5, + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 5, + 2, + 2 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 5, + 5, + 2, + 1, + 1 + ], + [ + 2, + 1, + 5, + 4, + 3 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 2, + 1, + 5, + 4, + 5 + ], + [ + 2, + 1, + 5, + 4, + 5 + ], + [ + 2, + 1, + 5, + 4, + 5 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 2, + 1, + 5, + 4, + 1 + ], + [ + 2, + 1, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1, + 3, + 5 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 1, + 2, + 2 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 2, + 1, + 4, + 1, + 1 + ], + [ + 1, + 2, + 3, + 3, + 2 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 3, + 1, + 1 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 1 + ], + [ + 5, + 4, + 1, + 4 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 5 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 5, + 4, + 3, + 2, + 2 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 5, + 4, + 5, + 1, + 5 + ], + [ + 5, + 4, + 5, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 5, + 3 + ], + [ + 1, + 5, + 1, + 3 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 1, + 5, + 3, + 4 + ], + [ + 2, + 3, + 3, + 5, + 5 + ], + [ + 2, + 3, + 3, + 1, + 5 + ], + [ + 2, + 3, + 3, + 1, + 5 + ], + [ + 2, + 3, + 3, + 5, + 5 + ], + [ + 2, + 3, + 3, + 5, + 5 + ], + [ + 2, + 3, + 3, + 5, + 5 + ], + [ + 2, + 3, + 3, + 5, + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 1, + 2, + 1 + ], + [ + 3, + 3, + 1, + 2, + 2 + ], + [ + 3, + 3, + 1, + 1, + 2 + ], + [ + 3, + 3, + 1, + 1, + 2 + ], + [ + 3, + 3, + 1, + 1, + 2 + ], + [ + 3, + 3, + 1, + 1, + 2 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 4, + 2, + 2 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 1, + 3 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 1 + ], + [ + 4, + 4, + 3 + ], + [ + 4, + 4, + 1, + 3 + ], + [ + 4, + 4, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 3, + 2, + 1, + 4, + 4 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2, + 1, + 1, + 1 + ], + [ + 3, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 2, + 1, + 1, + 5, + 2 + ], + [ + 2, + 1, + 1, + 5, + 2 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2, + 4, + 1, + 1, + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 3, + 1, + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 3, + 3, + 5 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 1, + 1, + 2, + 2 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 1, + 1, + 2, + 1 + ], + [ + 2, + 1, + 1, + 5, + 4 + ], + [ + 2, + 1, + 1, + 5, + 4 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 2, + 1, + 1, + 5, + 1 + ], + [ + 3, + 5, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2, + 3, + 2 + ], + [ + 2, + 3, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 5 + ], + [ + 1, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 2, + 5, + 3, + 5, + 4 + ], + [ + 2, + 5, + 3, + 5, + 4 + ], + [ + 2, + 5, + 3, + 3, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 4, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2, + 4, + 5, + 4, + 3 + ], + [ + 2, + 4, + 1, + 1, + 4 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 2, + 2, + 1 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 2, + 2, + 2, + 1, + 4 + ], + [ + 1, + 1, + 2, + 3, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 4, + 3 + ], + [ + 3, + 2, + 5, + 3 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 2, + 2, + 4 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 1, + 4, + 5, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 2, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 5, + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5 + ], + [ + 5, + 2, + 5, + 5, + 4 + ], + [ + 5, + 2, + 5, + 5, + 4 + ], + [ + 5, + 2, + 5, + 5, + 4 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 5, + 2, + 5, + 1, + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 2, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 2, + 1, + 1 + ], + [ + 4, + 4, + 2, + 4, + 1 + ], + [ + 4, + 4, + 2, + 4, + 1 + ], + [ + 4, + 4, + 2, + 4, + 1 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 4 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 3, + 3 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 3, + 5, + 1, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4, + 5 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 1 + ], + [ + 4, + 4 + ], + [ + 4, + 4 + ], + [ + 1, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 4 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 2, + 5, + 1 + ], + [ + 1, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 3, + 1, + 2, + 1, + 5 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 1, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 4 + ], + [ + 4, + 3, + 4, + 1 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 1, + 2 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 4, + 3, + 2, + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 4, + 1 + ], + [ + 1 + ], + [ + 2, + 5 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 2, + 1, + 5, + 3 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 3, + 3, + 1 + ], + [ + 4, + 1, + 5, + 4, + 5 + ], + [ + 4, + 1, + 5, + 4, + 5 + ], + [ + 4, + 1, + 5, + 4, + 5 + ], + [ + 4, + 1, + 5, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 4, + 1, + 3, + 1, + 1 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 5, + 5, + 3 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 2, + 4 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 5, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 2, + 2, + 1, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 4 + ], + [ + 1, + 4, + 1 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 2 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 1, + 1, + 1, + 5 + ], + [ + 2, + 4, + 5, + 4, + 1 + ], + [ + 2, + 4, + 5, + 4, + 1 + ], + [ + 2, + 4, + 5, + 4, + 1 + ], + [ + 2, + 4, + 2, + 5, + 5 + ], + [ + 2, + 5, + 2, + 5, + 5 + ], + [ + 2, + 5, + 2, + 5, + 5 + ], + [ + 2, + 5, + 2, + 5, + 5 + ], + [ + 1, + 4, + 3, + 5, + 3 + ], + [ + 1, + 4, + 3, + 1, + 2 + ], + [ + 1, + 4, + 3, + 1, + 2 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 1, + 4, + 1, + 1, + 1 + ], + [ + 4, + 3, + 4, + 2 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 3, + 4, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4, + 3, + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 1, + 4, + 5, + 4, + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 4, + 3, + 3, + 4, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 1, + 2, + 3, + 2, + 3 + ], + [ + 1, + 2, + 3, + 2, + 3 + ], + [ + 1, + 2, + 1, + 1, + 5 + ], + [ + 1 + ], + [ + 2, + 3 + ], + [ + 2, + 4 + ], + [ + 2, + 4 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 3 + ], + [ + 4, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 5, + 4, + 5 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 3, + 1 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 2, + 5, + 1, + 3 + ], + [ + 3, + 3, + 5, + 2, + 4 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 3, + 3, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 4, + 4 + ], + [ + 1, + 2 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 5, + 5, + 1 + ], + [ + 3, + 4, + 3, + 1, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 5, + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 5, + 1, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 2, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 5 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 3, + 5, + 2, + 1 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 1, + 5 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1, + 4, + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 3, + 1 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 5 + ], + [ + 1, + 5, + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 2, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 1, + 2, + 5, + 2 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1, + 2, + 5, + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 3 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 2, + 3, + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 1 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5, + 2 + ], + [ + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 4, + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 3, + 5, + 1, + 2 + ], + [ + 2, + 2, + 2 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 1, + 1, + 1, + 4 + ], + [ + 2, + 4, + 5 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 1, + 5 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 4, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 2, + 3, + 1, + 5, + 1 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 2, + 4, + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 1, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 2, + 4, + 1 + ], + [ + 4, + 2 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 4, + 3, + 4, + 2 + ], + [ + 4, + 4, + 3, + 4, + 2 + ], + [ + 4, + 4, + 3, + 4, + 2 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 3, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 4, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1, + 1 + ], + [ + 5, + 4, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 2, + 1 + ], + [ + 3, + 1, + 2 + ], + [ + 3, + 1, + 2 + ], + [ + 1, + 3 + ], + [ + 1, + 3 + ], + [ + 4, + 5, + 1, + 3, + 3 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 4, + 1, + 1, + 1, + 3 + ], + [ + 1, + 1, + 3, + 2, + 1 + ], + [ + 2, + 2 + ], + [ + 2, + 2 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 5, + 4, + 1, + 5, + 3 + ], + [ + 1, + 4, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 5, + 3, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 1, + 5, + 1, + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4, + 2, + 1, + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4, + 1, + 5, + 3 + ], + [ + 4, + 1, + 1, + 5 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 3, + 3 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 3 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 1, + 1, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 3, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 2, + 4 + ], + [ + 3, + 1, + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 3, + 5, + 1 + ], + [ + 3, + 3, + 5, + 3 + ], + [ + 3, + 1, + 1, + 1 + ], + [ + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 2, + 5, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4, + 5, + 5, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 4, + 5, + 1, + 1 + ], + [ + 5, + 5, + 3, + 3, + 3 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 4, + 3, + 1, + 5, + 4 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 3, + 1, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 5, + 4, + 5, + 3 + ], + [ + 1, + 2, + 4, + 3, + 2 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 4, + 1, + 3 + ], + [ + 1, + 2, + 1, + 1, + 5 + ], + [ + 1, + 2, + 1, + 1, + 5 + ], + [ + 2, + 1, + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5, + 2, + 2, + 5 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 2, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 4, + 3 + ], + [ + 3, + 4, + 1, + 1, + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 1, + 5 + ], + [ + 3, + 1, + 1 + ], + [ + 3, + 1, + 1 + ], + [ + 5, + 4, + 3, + 5 + ], + [ + 5, + 4, + 3, + 5 + ], + [ + 5, + 4, + 3, + 5 + ], + [ + 5, + 4, + 3, + 5 + ], + [ + 5, + 4, + 3, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1, + 1, + 1, + 3, + 2 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 3 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 5, + 1, + 1 + ], + [ + 5, + 1, + 5, + 4 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 2 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 3, + 1, + 1 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 2 + ], + [ + 4, + 3, + 1, + 1, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 1, + 1 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 5 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 3, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 4 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 5, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5, + 4, + 4, + 4, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 3 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 4 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 5, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3, + 3, + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 2, + 2, + 3, + 2 + ], + [ + 4, + 4, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 5, + 4, + 4 + ], + [ + 5, + 5, + 1, + 1, + 1 + ], + [ + 5, + 4, + 3, + 5, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 1, + 1, + 1, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 3, + 5, + 1 + ], + [ + 5, + 1, + 2, + 1 + ], + [ + 5, + 1, + 1, + 1 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 4 + ], + [ + 5, + 3, + 1 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 1, + 4 + ], + [ + 5, + 4, + 4 + ], + [ + 2, + 5 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 2, + 1, + 2, + 1 + ], + [ + 2, + 2, + 1, + 2, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 2, + 2, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 5, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 5 + ], + [ + 5, + 2, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 5, + 1, + 1 + ], + [ + 3, + 2, + 5 + ], + [ + 3, + 2, + 3 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 3, + 3, + 2 + ], + [ + 1, + 2, + 3, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 5, + 4 + ], + [ + 5, + 2, + 2, + 5, + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 4, + 5 + ], + [ + 5, + 1, + 1 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 5 + ], + [ + 1, + 3, + 5, + 4 + ], + [ + 1, + 3, + 5, + 4 + ], + [ + 1, + 3, + 5, + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 5, + 3 + ], + [ + 2, + 4, + 1, + 1, + 1 + ], + [ + 1, + 1, + 2, + 1, + 1 + ], + [ + 1, + 1, + 1, + 1, + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4, + 1, + 4 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 4, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 1 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 3, + 1, + 5, + 1, + 4 + ], + [ + 1, + 1, + 1, + 2, + 1 + ], + [ + 1, + 4, + 3 + ], + [ + 1, + 4, + 5 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 4, + 4 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 3, + 3, + 1 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 2, + 1, + 1, + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 3, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 3, + 2, + 1, + 5 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 2, + 4 + ], + [ + 2, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 2 + ], + [ + 2, + 2, + 1, + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 4, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 2, + 3 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 2, + 3, + 1 + ], + [ + 1, + 3, + 1 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 4, + 5 + ], + [ + 2, + 3, + 4, + 4, + 2 + ], + [ + 2, + 3, + 4, + 4, + 2 + ], + [ + 2, + 3, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 2, + 1, + 1, + 1, + 1 + ], + [ + 5, + 4, + 1 + ], + [ + 5, + 4, + 1, + 1 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 1, + 1, + 4, + 3 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 2, + 4, + 1 + ], + [ + 4, + 1, + 1, + 1, + 2 + ], + [ + 4, + 2, + 1, + 1, + 1 + ], + [ + 4, + 1, + 1, + 1, + 1 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 5, + 3 + ], + [ + 1, + 1, + 1 + ], + [ + 1, + 1, + 1 + ] + ], + "shape_input_tensor_s": [ + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 48 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 9 + ], + [ + 9 + ], + [ + 30 + ], + [ + 10 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 50 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 64 + ], + [ + 16 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 36 + ], + [ + 54 + ], + [ + 27 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 48 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 48 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 45 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 60 + ], + [ + 5 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 60 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 24 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 48 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 10 + ], + [ + 15 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 10 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 18 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 48 + ], + [ + 48 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 48 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 60 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 10 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 60 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 18 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 40 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 27 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 6 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 60 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 10 + ], + [ + 10 + ], + [ + 20 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 24 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 40 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 12 + ], + [ + 12 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 50 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 5 + ], + [ + 10 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 8 + ], + [ + 2 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 3 + ], + [ + 64 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 50 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 30 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 48 + ], + [ + 12 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 20 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 32 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 40 + ], + [ + 40 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 24 + ], + [ + 24 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 16 + ], + [ + 24 + ], + [ + 48 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 40 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 24 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 27 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 20 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 48 + ], + [ + 32 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 24 + ], + [ + 24 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 64 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 64 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 8 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 6 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 25 + ], + [ + 5 + ], + [ + 10 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 40 + ], + [ + 20 + ], + [ + 50 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 8 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 45 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 48 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 2 + ], + [ + 40 + ], + [ + 20 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 6 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 2 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 12 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 12 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 50 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 45 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 25 + ], + [ + 25 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 25 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 10 + ], + [ + 10 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 40 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 40 + ], + [ + 40 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 12 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 50 + ], + [ + 10 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 10 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 16 + ], + [ + 24 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 64 + ], + [ + 64 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 12 + ], + [ + 4 + ], + [ + 16 + ], + [ + 4 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 48 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 9 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 40 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 16 + ], + [ + 16 + ], + [ + 64 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 40 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 18 + ], + [ + 18 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 6 + ], + [ + 1 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 40 + ], + [ + 10 + ], + [ + 10 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 24 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 48 + ], + [ + 12 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 60 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 24 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 50 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 8 + ], + [ + 8 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 18 + ], + [ + 18 + ], + [ + 6 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 12 + ], + [ + 48 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 60 + ], + [ + 20 + ], + [ + 20 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 9 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 20 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 12 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 60 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 18 + ], + [ + 18 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 20 + ], + [ + 3 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 45 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 36 + ], + [ + 60 + ], + [ + 60 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 60 + ], + [ + 3 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 12 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 16 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 64 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 10 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 6 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 60 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 32 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 48 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 15 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 50 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 45 + ], + [ + 45 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 64 + ], + [ + 64 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 16 + ], + [ + 24 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 27 + ], + [ + 27 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 10 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 24 + ], + [ + 24 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 20 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 64 + ], + [ + 64 + ], + [ + 40 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 27 + ], + [ + 27 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 10 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 48 + ], + [ + 48 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 27 + ], + [ + 27 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 50 + ], + [ + 50 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 32 + ], + [ + 32 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 20 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 16 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 24 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 6 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 12 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 45 + ], + [ + 15 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 50 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 36 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 24 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 4 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 45 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 16 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 8 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 9 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 25 + ], + [ + 25 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 27 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 9 + ], + [ + 3 + ], + [ + 30 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 60 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 16 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 25 + ], + [ + 25 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 18 + ], + [ + 18 + ], + [ + 9 + ], + [ + 15 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 16 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 8 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 50 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 16 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 10 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 50 + ], + [ + 50 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 12 + ], + [ + 6 + ], + [ + 36 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 24 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 3 + ], + [ + 3 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 60 + ], + [ + 10 + ], + [ + 25 + ], + [ + 25 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 15 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 24 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 12 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 9 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 30 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 54 + ], + [ + 54 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 9 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 5 + ], + [ + 5 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 12 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 9 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 36 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 27 + ], + [ + 9 + ], + [ + 27 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 27 + ], + [ + 3 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 40 + ], + [ + 20 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 20 + ], + [ + 4 + ], + [ + 3 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 30 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 48 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 64 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 32 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 5 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 8 + ], + [ + 8 + ], + [ + 20 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 64 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 27 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 54 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 32 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 16 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 2 + ], + [ + 20 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 15 + ], + [ + 15 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 16 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 12 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 60 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 24 + ], + [ + 24 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 40 + ], + [ + 1 + ], + [ + 12 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 9 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 6 + ], + [ + 6 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 54 + ], + [ + 54 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 25 + ], + [ + 25 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 3 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 3 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 64 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 30 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 25 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 9 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 4 + ], + [ + 2 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 3 + ], + [ + 30 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 50 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 8 + ], + [ + 36 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 9 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 10 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 50 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 6 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 32 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 48 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 64 + ], + [ + 32 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 10 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 24 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 32 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 64 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 32 + ], + [ + 64 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 40 + ], + [ + 40 + ], + [ + 8 + ], + [ + 4 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 24 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 25 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 5 + ], + [ + 5 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 50 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 64 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 6 + ], + [ + 1 + ], + [ + 45 + ], + [ + 16 + ], + [ + 16 + ], + [ + 4 + ], + [ + 12 + ], + [ + 4 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 48 + ], + [ + 48 + ], + [ + 64 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 15 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 9 + ], + [ + 1 + ], + [ + 32 + ], + [ + 16 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 2 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 16 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 4 + ], + [ + 2 + ], + [ + 12 + ], + [ + 12 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 20 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 3 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 10 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 12 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 2 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 54 + ], + [ + 18 + ], + [ + 18 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 3 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 16 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 45 + ], + [ + 45 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 18 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 15 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 10 + ], + [ + 25 + ], + [ + 5 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 32 + ], + [ + 8 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 15 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 40 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 25 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 27 + ], + [ + 27 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 2 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 48 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 16 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 2 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 16 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 48 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 4 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 36 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 5 + ], + [ + 30 + ], + [ + 30 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 30 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 4 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 10 + ], + [ + 10 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 12 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 9 + ], + [ + 3 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 24 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 30 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 3 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 12 + ], + [ + 3 + ], + [ + 3 + ], + [ + 3 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 32 + ], + [ + 32 + ], + [ + 32 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 48 + ], + [ + 1 + ], + [ + 54 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 3 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 36 + ], + [ + 36 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 18 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 12 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 8 + ], + [ + 8 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 9 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 16 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 54 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 50 + ], + [ + 50 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 15 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 3 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 3 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 8 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 45 + ], + [ + 3 + ], + [ + 5 + ], + [ + 5 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 3 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 25 + ], + [ + 25 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 50 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 15 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 40 + ], + [ + 40 + ], + [ + 40 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 5 + ], + [ + 2 + ], + [ + 2 + ], + [ + 2 + ], + [ + 4 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 10 + ], + [ + 20 + ], + [ + 20 + ], + [ + 20 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 60 + ], + [ + 60 + ], + [ + 60 + ], + [ + 36 + ], + [ + 36 + ], + [ + 36 + ], + [ + 64 + ], + [ + 64 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 5 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 6 + ], + [ + 6 + ], + [ + 6 + ], + [ + 2 + ], + [ + 1 + ], + [ + 1 + ], + [ + 1 + ], + [ + 4 + ], + [ + 4 + ], + [ + 4 + ], + [ + 5 + ], + [ + 1 + ] + ], + "input_type": [ + "INT8", + "FP16", + "INT8", + "UINT32", + "UINT64", + "UINT8", + "FP32", + "UINT32", + "FP32", + "UINT16", + "BOOL", + "BOOL", + "INT64", + "UINT16", + "FP64", + "INT32", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "UINT32", + "INT64", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT64", + "INT8", + "STRING", + "STRING", + "UINT16", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT8", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "FP32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "UINT64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP32", + "FP32", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "BOOL", + "BOOL", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP32", + "FP32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP32", + "FP32", + "FP32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT16", + "UINT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT32", + "UINT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP32", + "FP32", + "FP32", + "FP32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT32", + "UINT32", + "UINT32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT64", + "INT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "INT16", + "INT16", + "INT16", + "INT16", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP32", + "FP32", + "FP32", + "FP32", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "UINT8", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "INT32", + "UINT16", + "STRING", + "STRING", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "UINT32", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "UINT64", + "BOOL", + "BOOL", + "BOOL", + "INT16", + "INT16", + "INT16", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "BOOL", + "FP64", + "FP64", + "FP64", + "FP64", + "FP64", + "INT64", + "INT64", + "INT64", + "INT64", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "STRING", + "STRING", + "STRING", + "STRING", + "STRING", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "FP16", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "INT8", + "UINT16", + "UINT16", + "UINT16", + "UINT16", + "UINT16" + ], + "ONNXRuntime_Provider": "CPUExecutionProvider" +} \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/reshape.py b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/reshape.py new file mode 100644 index 00000000..a5265097 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/reshape.py @@ -0,0 +1,32 @@ +import numpy as np + +def size (dims): + result = 1 + for x in dims: + result = result * x + return result + +def offset(ks, ds): + if ks and ds: + k, ks_rest = ks[0], ks[1:] + ds_rest = ds[1:] + return k * size(ds_rest) + offset(ks_rest, ds_rest) + else: + return 0 + +def index(p, ds): + if ds: + _, ds_rest = ds[0], ds[1:] + return [p // size(ds_rest)] + index(p % size(ds_rest), ds_rest) + else: + return [] + + +def reshape(X,Y): + for idx in np.ndindex(Y.shape): + idx_list = list(idx) + y_coords_flat = offset(idx_list, list(Y.shape)) + x_coords = index(y_coords_flat, list(X.shape)) + Y[idx] = X[tuple(x_coords)] + return Y + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/test_reshape.py b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/test_reshape.py new file mode 100644 index 00000000..7133c09d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/reshape/hypothesis/test_reshape.py @@ -0,0 +1,401 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Reshape operation in ONNX. +""" +import os + +import math +import json +import numpy as np + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession + + +from onnx import helper + +import tensorflow as tf + +from sympy import divisors + +from reshape import reshape + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Inputs/attributes for Reshape operation +""" + +inputs_attributes = { + "min_rank_input": 1, #Adjust as needed + "max_rank_input": 5, #Adjust as needed + "min_dim_size_input": 1, #Adjust as needed + "max_dim_size_input": 5, #Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + +""" +Reshape supported types, organized by ONNXRuntime_Provider +""" +reshape_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + +dtype_to_key = {v: k for k, v in reshape_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Store generated data +""" +generated_data = { + "rank_input_tensor_x": [], + "shape_input_tensor_x": [], + "rank_input_tensor_s": [], + "shape_input_tensor_s": [], + "input_type": [] +} + + +def calculate_y_shape(allowzero, data_tensor_shape, shape_input_tensor): + """ + Function to calculate expected output shape after Reshape operation + """ + y_shape = [] + for i, dim in enumerate(data_tensor_shape): + if allowzero == 0 and dim == 0 and i < len(shape_input_tensor): + y_shape.append(int(shape_input_tensor[i])) + elif dim == -1: + prod_known_dims = 1 + for j, d in enumerate(data_tensor_shape): + if j != i: + if d == 0 and j < len(shape_input_tensor) and allowzero == 0: + prod_known_dims *= shape_input_tensor[j] + else: + prod_known_dims *= d + inferred_dim = int(np.prod(shape_input_tensor) // prod_known_dims) + y_shape.append(inferred_dim) + else: + y_shape.append(int(dim)) + return y_shape + + + +def valid_shape_values_allowzero_one(shape_input_tensor, num_shape_elements, draw): + """ + Function to generate valid shape tensor values when allowzero is 1 + """ + data_tensor_shape = [] + total_shape = np.prod(shape_input_tensor) + possible_dims = [] + for _ in range(num_shape_elements - 1): + possible_dims = divisors(total_shape) + if 0 in shape_input_tensor: + possible_dims.append(0) + dim = draw(st.sampled_from(possible_dims)) + data_tensor_shape.append(dim) + if dim != 0: + total_shape = total_shape // dim + data_tensor_shape.append(total_shape) + return data_tensor_shape + + +def valid_shape_values_allowzero_zero(shape_input_tensor, rank_input_tensor, num_shape_elements, draw): + """ + Function to generate valid shape tensor values when allowzero is 0 + """ + data_tensor_shape = [] + total_shape = np.prod(shape_input_tensor) + possible_dims = [] + for i in range(num_shape_elements - 1): + # Allowzero [C2], S[C2] + if i < rank_input_tensor and shape_input_tensor[i] in divisors(total_shape): + possible_dims = divisors(total_shape) + [0] + else: + possible_dims = divisors(total_shape) + dim = draw(st.sampled_from(possible_dims)) + data_tensor_shape.append(dim) + if dim != 0: + total_shape = total_shape // dim + if dim == 0 and i < len(shape_input_tensor): + total_shape = total_shape // shape_input_tensor[i] + data_tensor_shape.append(total_shape) + return data_tensor_shape + +""" +Function to generate valid reshape arguments +""" +@st.composite +@settings() +def valid_reshape_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + + #TODO Change contraints numbers + # X [C2] - Input/Output Types Consistency + all_valid_types = list(reshape_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + input_type = draw(st.sampled_from(all_valid_types)) + input_dtype = reshape_types.get(inputs_attributes["ONNXRuntime_Provider"])[input_type] + + if np.issubdtype(input_dtype, np.integer): + min_val = np.iinfo(input_dtype).min + max_val = np.iinfo(input_dtype).max + input_strategy = st.integers(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.floating): + min_val = np.finfo(input_dtype).min + max_val = np.finfo(input_dtype).max + input_strategy = st.floats(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.bool_): + input_strategy = st.booleans() + elif np.issubdtype(input_dtype, np.str_): + input_strategy = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + rank_input_tensor = draw(st.integers( + min_value=inputs_attributes["min_rank_input"], + max_value=inputs_attributes["max_rank_input"] + )) + + shape_input_tensor = [] + for _ in range(rank_input_tensor): + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_size_input"], + max_value=inputs_attributes["max_dim_size_input"] + )) + shape_input_tensor.append(dim_size) + + x = draw(hnp.arrays(dtype=input_dtype, shape=shape_input_tensor, elements=input_strategy)) + + #--------------------------------------------------- + # Attribute allowzero + #--------------------------------------------------- + + # Allowzero [C1] + allowzero = draw(st.integers( + min_value=0, + max_value=1 + )) + + #--------------------------------------------------- + # Input S + #--------------------------------------------------- + rank_tensor_shape = draw(st.integers( + min_value=inputs_attributes["min_rank_input"], + max_value=inputs_attributes["max_rank_input"] + )) + + shape_tensor_shape = [] + for _ in range(rank_tensor_shape): + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_size_input"], + max_value=inputs_attributes["max_dim_size_input"] + )) + shape_tensor_shape.append(dim_size) + + # ONNX Runtime limit the total number of dimensions, on shape tensor, to 64 + assume(np.prod(shape_tensor_shape) <= 64) + + num_shape_elements = np.prod(shape_tensor_shape) + + if allowzero == 1: + data_tensor_shape = valid_shape_values_allowzero_one(shape_input_tensor, num_shape_elements, draw) + else: + data_tensor_shape = valid_shape_values_allowzero_zero(shape_input_tensor, rank_input_tensor, num_shape_elements, draw) + + # Allowzero [C3], S [C1], S [C3] + allow_infer = draw(st.booleans()) + if allow_infer and ((allowzero == 0 and 0 not in shape_input_tensor) or (allowzero == 1 and 0 not in data_tensor_shape)): + infer_index = draw(st.integers( + min_value=0, + max_value=num_shape_elements - 1 + )) + data_tensor_shape[infer_index] = -1 + + shape_tensor = np.array(data_tensor_shape, dtype=np.int64) + + y_shape = calculate_y_shape(allowzero, data_tensor_shape, shape_input_tensor) + + return x, shape_tensor, y_shape, allowzero + + +@settings(max_examples=50000, deadline=None) +@given(valid_reshape_args()) +def test_reshape(args): + x, shape_tensor, y_shape, allowzero = args + generated_data["rank_input_tensor_x"].append(len(x.shape)) + generated_data["shape_input_tensor_x"].append(list(x.shape)) + generated_data["rank_input_tensor_s"].append(len(shape_tensor.shape)) + generated_data["shape_input_tensor_s"].append(list(shape_tensor.shape)) + input_type_key = dtype_to_key.get(x.dtype.type, str(x.dtype)) + generated_data["input_type"].append(input_type_key) + y = run_onnx_reshape_test(x, shape_tensor, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + check_constraints(x, shape_tensor, allowzero, y, y_shape) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated by Hypothesis for Reshape operation tests", + "min_rank_input": inputs_attributes["min_rank_input"], + "max_rank_input": inputs_attributes["max_rank_input"], + "rank_input_tensor_x": generated_data["rank_input_tensor_x"], + "rank_input_tensor_s": generated_data["rank_input_tensor_s"], + "min_dim_size_input": inputs_attributes["min_dim_size_input"], + "max_dim_size_input": inputs_attributes["max_dim_size_input"], + "shape_input_tensor_x": generated_data["shape_input_tensor_x"], + "shape_input_tensor_s": generated_data["shape_input_tensor_s"], + "input_type": generated_data["input_type"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"] + } + + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def run_onnx_reshape_test(x, s, y_shape, provider): + """ + Function that runs the ONNX Reshape operation + """ + x_onnx = helper.make_tensor_value_info('x', helper.np_dtype_to_tensor_dtype(x.dtype), x.shape) + s_onnx = helper.make_tensor_value_info('s', helper.np_dtype_to_tensor_dtype(s.dtype), s.shape) + + y_onnx = helper.make_tensor_value_info('y', helper.np_dtype_to_tensor_dtype(x.dtype), y_shape) + + node_def = helper.make_node( + 'Reshape', + inputs=['x', 's'], + outputs=['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test_reshape', + [x_onnx, s_onnx], + [y_onnx], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'x': x, 's': s})[0] + print("y shape:", y.shape) + print("y dtype:", y.dtype) + print("y:", y) + return y + + +def clamp_s_tensor(x,s,allowzero): + """ + Function to clamp shape tensor values to valid ones + """ + clamped_s = [] + for i, dim in enumerate(s): + if allowzero == 0 and dim == 0 and i < len(x.shape): + clamped_s.append(x.shape[i]) + else: + clamped_s.append(dim) + x_prod = np.prod(x.shape) + s_prod = np.prod([dim for dim in clamped_s if dim != -1]) + if -1 in clamped_s: + inferred_dim = x_prod // s_prod + clamped_s = [inferred_dim if dim == -1 else dim for dim in clamped_s] + return clamped_s + + +def check_constraints(x, s, allowzero, y, y_shape): + """ + Function to check constraints after Reshape operation + """ + # Y[C1] - Shape consistency + assert np.prod(x.shape) == np.prod(y.shape) == np.prod(clamp_s_tensor(x,s,allowzero)) + + #Allowzero [C1] + assert allowzero in [0,1] + + #Allowzero [C2] + if allowzero == 0: + for i, s_value in enumerate(s): + if s_value == 0: + assert i < len(x.shape) + + # Allowzero [C3] + if allowzero == 0 and -1 in s: + assert 0 not in x.shape + + if allowzero == 1: + for s_value in s: + assert s_value > 0 or (s_value == 0 and 0 in list(x.shape) and not -1 in s) or (s_value == -1 and 0 not in s) + + # S [C1] + assert list(s).count(-1) <= 1 + + expected_result = reshape(x,y) + assert np.array_equal(y, expected_result) diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.ipynb b/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.ipynb new file mode 100644 index 00000000..4bf25e54 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.ipynb @@ -0,0 +1,876 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 2, + "id": "02a475ca", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/jcm-machado/venv/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/jcm-machado/venv/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/jcm-machado/venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m25.3\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m26.0.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input1_name = \"X\"\n", + "input2_name = \"shape\"\n", + "\n", + "reshape_output_name = \"Y\"\n", + "\n", + "\n", + "# Create the ONNX model with Reshape operator\n", + "def create_reshape_model(input_rank, out_shape, dtype, allowzero=0):\n", + "\n", + " # Create \"input-rank\" input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input1_name, dtype, input_rank)\n", + " # Create \"shape\" input tensor (1D tensor of INT64)\n", + " input2 = onnx.helper.make_tensor_value_info(input2_name, onnx.TensorProto.INT64, [None])\n", + "\n", + " # Create output tensor (final result after reshape operation)\n", + " reshape_output = onnx.helper.make_tensor_value_info(reshape_output_name, dtype, out_shape)\n", + "\n", + " # Define reshape node\n", + " reshape_node = onnx.helper.make_node(\n", + " \"Reshape\",\n", + " inputs=[input1_name, input2_name],\n", + " allowzero=allowzero,\n", + " outputs=[reshape_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph (no output shape specified - will be inferred at runtime)\n", + " graph_def = onnx.helper.make_graph(\n", + " [reshape_node],\n", + " \"Reshape\",\n", + " [input1, input2],\n", + " [reshape_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 21)])\n", + " model.ir_version = 9 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"reshape.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"reshape.onnx\")\n", + " return session\n", + "\n", + "def do_reshape(x, shape, session):\n", + " # Run inference\n", + " output = session.run(None, {input1_name: x, input2_name: shape})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(f\"X shape: {x.shape}, X={x_f}\")\n", + " print(f\"Output shape: {output[0].shape}, Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Simple cases" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "87196b1c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (5,), X=[-2,-1, 0, 1, 2]\n", + "Output shape: (5,), Result = [-2,-1, 0, 1, 2]\n" + ] + } + ], + "source": [ + "# Case N1\n", + "# Input tensor: single rank tensor (int32)\n", + "# Shape tensor: [5] - 1D tensor with the length of the input tensor\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([-2, -1, 0, 1, 2], dtype=np.int32)\n", + "shape = [5]\n", + "session = create_reshape_model([None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "638b9c9d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 5), X=[[0,1,2,3,4], [5,6,7,8,9]]\n", + "Output shape: (5, 2), Result = [[0,1], [2,3], [4,5], [6,7], [8,9]]\n" + ] + } + ], + "source": [ + "# Case N2\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [5, 2] - the shape is the inverted shape of the input tensor\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[0, 1, 2, 3, 4],[5, 6, 7, 8, 9]], dtype=np.int32)\n", + "shape = [5, 2]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8a4e6329", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (4, 5), X=[[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n", + "Output shape: (10, 2), Result = [[ 1, 2], [ 3, 4], [ 5, 6], [ 7, 8], [ 9,10], [11,12], [13,14], [15,16], [17,18], [19,20]]\n" + ] + } + ], + "source": [ + "# Case N3 - Test1\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Shape tensor: [10, 2]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [10, 2]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "78867c4a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (4, 5), X=[[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n", + "Output shape: (5, 2, 2), Result = [[[ 1, 2], [ 3, 4]], [[ 5, 6], [ 7, 8]], [[ 9,10], [11,12]], [[13,14], [15,16]], [[17,18], [19,20]]]\n" + ] + } + ], + "source": [ + "# Case N3 - Test2\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Shape tensor: [5, 4]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [5, 2, 2]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "baf1eb00", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (4, 5), X=[[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n", + "Output shape: (20, 1), Result = [[ 1], [ 2], [ 3], [ 4], [ 5], [ 6], [ 7], [ 8], [ 9], [10], [11], [12], [13], [14], [15], [16], [17], [18], [19], [20]]\n" + ] + } + ], + "source": [ + "# Case N3 - Test3\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [20, 1]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [20, 1]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "223f48c0", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-03-09 10:51:37.974706536 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{4,5}, requested shape:{6,4}\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{4,5}, requested shape:{6,4}\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m 8\u001b[39m session = create_reshape_model([\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m], shape, onnx_type)\n\u001b[32m 9\u001b[39m shape = np.array(shape, dtype=np.int64)\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[43mdo_reshape\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 53\u001b[39m, in \u001b[36mdo_reshape\u001b[39m\u001b[34m(x, shape, session)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_reshape\u001b[39m(x, shape, session):\n\u001b[32m 52\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput1_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput2_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 56\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{4,5}, requested shape:{6,4}\n" + ] + } + ], + "source": [ + "# This test MUST FAIL! (the shape does not match the number of elements)\n", + "# Case N3 - Test4\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [6, 4] - Not compatinble (it would only be possible if we had 24 elements, we have 20)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [6, 4]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "markdown", + "id": "c5773a04", + "metadata": {}, + "source": [ + "## Using (-1) to autoinfer one dimension" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f266bd4c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (5,), X=[-2,-1, 0, 1, 2]\n", + "Output shape: (5,), Result = [-2,-1, 0, 1, 2]\n" + ] + } + ], + "source": [ + "# Case N1\n", + "# Input tensor: single rank tensor (int32)\n", + "# Shape tensor: [-1] -- coverts to --> [5] (Auto infered dimension)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([-2, -1, 0, 1, 2], dtype=np.int32)\n", + "shape = [-1]\n", + "session = create_reshape_model([None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "25cc2051", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 5), X=[[-20,-10, 0, 10, 20], [-15,-10, 5, 10, 0]]\n", + "Output shape: (5, 2), Result = [[-20,-10], [ 0, 10], [ 20,-15], [-10, 5], [ 10, 0]]\n" + ] + } + ], + "source": [ + "# Case N2\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [5, -1] -- coverts to --> [5, 2] (Auto infered dimension)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[-20,-10,0,10,20],[-15,-10,5,10,0]], dtype=np.int32)\n", + "shape = [5, -1]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "78e79fc3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (4, 5), X=[[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n", + "Output shape: (10, 2), Result = [[ 1, 2], [ 3, 4], [ 5, 6], [ 7, 8], [ 9,10], [11,12], [13,14], [15,16], [17,18], [19,20]]\n" + ] + } + ], + "source": [ + "# Case N3 - Test1\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [10, -1] -- coverts to --> [10, 2] (Auto infered dimension)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [10, -1]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "71b585a3", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-03-09 10:51:38.951117749 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:24 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) unknown_dim == -1 was false. At most one dimension can be -1.\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:24 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) unknown_dim == -1 was false. At most one dimension can be -1.\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[13]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m 8\u001b[39m session = create_reshape_model([\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m], shape, onnx_type)\n\u001b[32m 9\u001b[39m shape = np.array(shape, dtype=np.int64)\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[43mdo_reshape\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 53\u001b[39m, in \u001b[36mdo_reshape\u001b[39m\u001b[34m(x, shape, session)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_reshape\u001b[39m(x, shape, session):\n\u001b[32m 52\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput1_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput2_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 56\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:24 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) unknown_dim == -1 was false. At most one dimension can be -1.\n" + ] + } + ], + "source": [ + "# This test MUST FAIL! (only one dimension can be auto inferred)\n", + "# Case N3 - Test2\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Shape tensor: [-1, -1] --> conversion not allowed\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "shape = [-1, -1]\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "5d54dbe0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 3, 2, 2), X=[[[[ 0, 1], [ 2, 3]], [[ 4, 5], [ 6, 7]], [[ 8, 9], [10,11]]], [[[12,13], [14,15]], [[16,17], [18,19]], [[20,21], [22,23]]]]\n", + "Output shape: (2, 6, 2), Result = [[[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7], [ 8, 9], [10,11]], [[12,13], [14,15], [16,17], [18,19], [20,21], [22,23]]]\n" + ] + } + ], + "source": [ + "# Case N4\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Shape tensor: [0, 6, -1] -- coverts to --> [2, 6, 4] (Auto infered dimension)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24, dtype=np.int32).reshape(2, 3, 2, 2)\n", + "shape = [0, 6, -1]\n", + "session = create_reshape_model([None, None, None, None], [2, 6, 2], onnx_type, allowzero=0)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "markdown", + "id": "c8304096", + "metadata": {}, + "source": [ + "## Using **allowzero**\n", + "\n", + "In this section the shape will be explicitly passed to the `create_reshape_model`.\n", + "\n", + "However, `do_reshape` is taking into account the intended shape." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "05b43cb2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (5,), X=[-2,-1, 0, 1, 2]\n", + "Output shape: (5,), Result = [-2,-1, 0, 1, 2]\n" + ] + } + ], + "source": [ + "# Case N1: Test1\n", + "# Input tensor: single rank tensor (int32)\n", + "# Allowzero: If not specified its value is 0\n", + "# Shape tensor: [0] -- converts to --> [5] (auto infered dimension from the input tensor)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([-2, -1, 0, 1, 2], dtype=np.int32)\n", + "session = create_reshape_model([None], [5], onnx_type)\n", + "shape = [0]\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "cfb5f4f5", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-03-09 10:51:39.551449155 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{5}, requested shape:{5,0}\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{5}, requested shape:{5,0}\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[16]\u001b[39m\u001b[32m, line 11\u001b[39m\n\u001b[32m 9\u001b[39m shape = [\u001b[32m5\u001b[39m,\u001b[32m0\u001b[39m]\n\u001b[32m 10\u001b[39m shape = np.array(shape, dtype=np.int64)\n\u001b[32m---> \u001b[39m\u001b[32m11\u001b[39m \u001b[43mdo_reshape\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 53\u001b[39m, in \u001b[36mdo_reshape\u001b[39m\u001b[34m(x, shape, session)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_reshape\u001b[39m(x, shape, session):\n\u001b[32m 52\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput1_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput2_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 56\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:45 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) input_shape_size == size was false. The input tensor cannot be reshaped to the requested shape. Input shape:{5}, requested shape:{5,0}\n" + ] + } + ], + "source": [ + "# This cell MUST FAIL!\n", + "# Case N1: Test2\n", + "# Input tensor: single rank tensor (int32)\n", + "# Allowzero: 1\n", + "# Shape tensor: [0] (it is not possible to infer the dimension when allowzero is not 0)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([-2, -1, 0, 1, 2], dtype=np.int32)\n", + "session = create_reshape_model([None], [5], onnx_type, allowzero=1)\n", + "shape = [5,0]\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "976e3ea3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (4, 5), X=[[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n", + "Output shape: (4, 5), Result = [[ 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10], [11,12,13,14,15], [16,17,18,19,20]]\n" + ] + } + ], + "source": [ + "# Case N2\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Allowzero: If not specified its value is 0\n", + "# Shape tensor: [0, 0] -- converts to --> [4, 5] (auto infered dimension from the input tensor)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 13, 14, 15], [16, 17, 18, 19, 20]], dtype=np.int32)\n", + "session = create_reshape_model([None, None], [4, 5], onnx_type)\n", + "shape = [0, 0]\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "248b28e1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 5), X=[[0,1,2,3,4], [5,6,7,8,9]]\n", + "Output shape: (2, 5), Result = [[0,1,2,3,4], [5,6,7,8,9]]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-03-09 10:51:39.872832468 [W:onnxruntime:, execution_frame.cc:876 VerifyOutputSizes] Expected shape from model of {4,5} does not match actual shape of {2,5} for output Y\u001b[m\n" + ] + } + ], + "source": [ + "# Case N3\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Allowzero: If not specified its value is 0\n", + "# Shape tensor: [0, -1] -- converts to --> [4, 5]\n", + "# (0 is infered from input tensor)\n", + "# (-1 is auto infered based on the other dimensions)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]], dtype=np.int32)\n", + "session = create_reshape_model([None, None], [4, 5], onnx_type)\n", + "shape = [0, -1]\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "b82a8fef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (0, 3, 8), X=[]\n", + "Output shape: (1, 2, 0), Result = []\n" + ] + } + ], + "source": [ + "# Case N4\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Shape tensor: [0, -1] -- converts to --> [4, 5]\n", + "# (0 is infered from input tensor)\n", + "# (-1 is auto infered based on the other dimensions)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([], dtype=np.int32).reshape(0, 3, 8)\n", + "shape = [1, 2, 0]\n", + "session = create_reshape_model([None, None, None], shape, onnx_type, allowzero=1)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "markdown", + "id": "49cc692c", + "metadata": {}, + "source": [ + "## Using empty shape (converting to scalar)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "a64d5d0e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (1, 1), X=[[1]]\n", + "Output shape: (), Result = 1\n" + ] + } + ], + "source": [ + "# Case N1\n", + "# Input tensor: 2-rank tensor (int32)\n", + "# Allowzero: If not specified its value is 0\n", + "# Shape tensor: [] -- converts to --> scalar\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[1]], dtype=np.int32)\n", + "shape = []\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "9cb4417a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (1, 1, 1, 1), X=[[[[1]]]]\n", + "Output shape: (), Result = 1\n" + ] + } + ], + "source": [ + "# Case N2\n", + "# Input tensor: 4-rank tensor (int32) with 1 element\n", + "# Shape tensor: [] -- converts to --> scalar\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([[[[1]]]], dtype=np.int32)\n", + "shape = []\n", + "session = create_reshape_model([None, None, None, None], shape, onnx_type)\n", + "shape = np.array([], dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "7c705e6b", + "metadata": {}, + "outputs": [ + { + "ename": "InvalidArgument", + "evalue": "[ONNXRuntimeError] : 2 : INVALID_ARGUMENT : Invalid rank for input: X Got: 1 Expected: 2 Please fix either the inputs/outputs or the model.", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mInvalidArgument\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[22]\u001b[39m\u001b[32m, line 10\u001b[39m\n\u001b[32m 8\u001b[39m session = create_reshape_model([\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m], shape, onnx_type)\n\u001b[32m 9\u001b[39m shape = np.array(shape, dtype=np.int64)\n\u001b[32m---> \u001b[39m\u001b[32m10\u001b[39m \u001b[43mdo_reshape\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 53\u001b[39m, in \u001b[36mdo_reshape\u001b[39m\u001b[34m(x, shape, session)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_reshape\u001b[39m(x, shape, session):\n\u001b[32m 52\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput1_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput2_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 56\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mInvalidArgument\u001b[39m: [ONNXRuntimeError] : 2 : INVALID_ARGUMENT : Invalid rank for input: X Got: 1 Expected: 2 Please fix either the inputs/outputs or the model." + ] + } + ], + "source": [ + "# This cell MUST FAIL!\n", + "# Case N3 - Empty shape test with float32 (converts to scalar)\n", + "# Input tensor: 1-element tensor (float32)\n", + "# Shape tensor: [] (empty) -- converts to --> scalar (rank 0)\n", + "onnx_type = onnx.TensorProto.FLOAT\n", + "x = np.array([1, 2], dtype=np.float32)\n", + "shape = []\n", + "session = create_reshape_model([None, None], shape, onnx_type)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "abe42337", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 4, 0, 4, 2, 5, 2, 4, 5), X=[]\n", + "Output shape: (0, 0, 0, 0, 0), Result = []\n" + ] + } + ], + "source": [ + "# This cell MUST FAIL!\n", + "# Case N1: Test2\n", + "# Input tensor: single rank tensor (int32)\n", + "# Allowzero: 1\n", + "# Shape tensor: [0] (it is not possible to infer the dimension when allowzero is not 0)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.zeros((2, 4, 0, 4, 2, 5, 2, 4, 5), dtype=np.float32) # shape: (2, 0, 3)\n", + "shape = [0, 0, 0, 0, 0] # Mantém as dimensões zero\n", + "session = create_reshape_model([None, None, None, None, None, None, None, None, None], shape, onnx.TensorProto.FLOAT, allowzero=1)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "3dfc940e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (2, 4, 0), X=[]\n", + "Output shape: (2, 4, 0), Result = []\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[0;93m2026-03-09 10:51:42.603432319 [W:onnxruntime:, execution_frame.cc:876 VerifyOutputSizes] Expected shape from model of {0,0,0} does not match actual shape of {2,4,0} for output Y\u001b[m\n" + ] + } + ], + "source": [ + "# This cell MUST FAIL!\n", + "# Case N1: Test2\n", + "# Input tensor: single rank tensor (int32)\n", + "# Allowzero: 1\n", + "# Shape tensor: [0] (it is not possible to infer the dimension when allowzero is not 0)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.zeros((2, 4, 0), dtype=np.float32) # shape: (2, 0, 3)\n", + "shape = [0,0,0] # Mantém as dimensões zero\n", + "session = create_reshape_model([None, None, None], shape, onnx.TensorProto.FLOAT, allowzero=0)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "a0eb3f42", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2026-03-09 10:51:42.919452225 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:39 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) size != 0 && (input_shape_size % size) == 0 was false. The input tensor cannot be reshaped to the requested shape. Input shape:{25,1,4,1}, requested shape:{2,-1,0}\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:39 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) size != 0 && (input_shape_size % size) == 0 was false. The input tensor cannot be reshaped to the requested shape. Input shape:{25,1,4,1}, requested shape:{2,-1,0}\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[25]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m 7\u001b[39m session = create_reshape_model([\u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m, \u001b[38;5;28;01mNone\u001b[39;00m], shape, onnx_type, allowzero = \u001b[32m1\u001b[39m)\n\u001b[32m 8\u001b[39m shape = np.array(shape, dtype=np.int64)\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43mdo_reshape\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 53\u001b[39m, in \u001b[36mdo_reshape\u001b[39m\u001b[34m(x, shape, session)\u001b[39m\n\u001b[32m 51\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_reshape\u001b[39m(x, shape, session):\n\u001b[32m 52\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m53\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput1_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput2_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mshape\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 55\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 56\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venv/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Reshape node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/cpu/tensor/reshape_helper.h:39 onnxruntime::ReshapeHelper::ReshapeHelper(const onnxruntime::TensorShape&, onnxruntime::TensorShapeVector&, bool) size != 0 && (input_shape_size % size) == 0 was false. The input tensor cannot be reshaped to the requested shape. Input shape:{25,1,4,1}, requested shape:{2,-1,0}\n" + ] + } + ], + "source": [ + "# Case N1\n", + "# Input tensor: single rank tensor (int32)\n", + "# Shape tensor: [5] - 1D tensor with the length of the input tensor\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(100, dtype=np.int32).reshape(25, 1, 4, 1)\n", + "shape = [2, -1, 0]\n", + "session = create_reshape_model([None, None, None, None], shape, onnx_type, allowzero = 1)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ba3114f1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X shape: (0, 3, 8), X=[]\n", + "Output shape: (1, 2, 0), Result = []\n" + ] + } + ], + "source": [ + "# Case N4\n", + "# Input tensor: 3-rank tensor (int32)\n", + "# Shape tensor: [0, -1] -- converts to --> [4, 5]\n", + "# (0 is infered from input tensor)\n", + "# (-1 is auto infered based on the other dimensions)\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.array([], dtype=np.int32).reshape(0, 3, 8)\n", + "shape = [1, 2, -1]\n", + "session = create_reshape_model([None, None, None], shape, onnx_type, allowzero=1)\n", + "shape = np.array(shape, dtype=np.int64)\n", + "do_reshape(x, shape, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.onnx b/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.onnx new file mode 100644 index 00000000..401a47ac Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/reshape/reshape.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/READEME.md b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/READEME.md new file mode 100644 index 00000000..8372862f --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/READEME.md @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_shape.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_shape.py you need to do +```bash +pytest test_shape.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_shape.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_shape.py + +# Explanations of Shape corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Shape tests +For the shape operator we just **checked both the corner and edge cases** for the **rank_input_tensor**, **shape_input_tensor**, **x_type**, **start** and **end**. + +We consider **rank_input_tensor**, **shape_input_tensor**, **x_type**, **start** and **end** as **lines** since they are independent and we checked the corner and edge cases for both of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/check_edges.py new file mode 100644 index 00000000..6c8d6a8b --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/check_edges.py @@ -0,0 +1,128 @@ +""" +This file checks edge cases in the generated data for the Shape operation in ONNX. +""" +import json +import numpy as np +import ml_dtypes + +""" +Shape supported types, organized by ONNXRuntime_Provider +""" +shape_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + check_rank_input = check_individual_variables( + "rank_input_tensor", data["rank_input_tensor"], + data["min_rank_input"], data["max_rank_input"]) + + check_shape_size_input = check_individual_variables( + "shape_input_tensor", + [size for shape in data["shape_input_tensor"] for size in shape], + data["min_dim_size_input"], data["max_dim_size_input"]) + + provider = data["ONNXRuntime_Provider"] + + check_x_type = check_type("x_type", data["x_type"], provider) + + check_start = check_individual_variables( + "start", data["start"], + data["start_min"], data["start_max"]) + + check_end = check_individual_variables( + "end", data["end"], + data["end_min"], data["end_max"]) + + return all([check_rank_input, check_shape_size_input, check_x_type, check_start, check_end]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + + + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(shape_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + + +print(check_edges()) \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/test_shape.py b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/test_shape.py new file mode 100644 index 00000000..e1e906db --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/shape/hypothesis/test_shape.py @@ -0,0 +1,316 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Shape operation in ONNX. +""" +import os + +import json +import numpy as np +import ml_dtypes + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + + +from onnx import helper + +import tensorflow as tf + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + + +""" +Inputs/attributes for Shape operation +""" +inputs_attributes = { + "min_rank_input": 0, #Adjust as needed + "max_rank_input": 10, #Adjust as needed + "min_dim_size_input": 0, #Adjust as needed + "max_dim_size_input": 10, #Adjust as needed + "start_min": -50, #Adjust as needed + "start_max": 50, #Adjust as needed + "end_min": -50, #Adjust as needed + "end_max": 50, #Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + + +""" +Shape supported types, organized by ONNXRuntime_Provider +""" +# We remove from CPUExecutionProvider the BFLOAT16 type. ONNXRutime does not support BFLOAT16 while using numpy. +# We tried to use the reference implementation from ONNX, but it was giving wrong results so we remove it. +shape_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + +dtype_to_key = {v: k for k, v in shape_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Store generated data +""" +generated_data = { + "rank_input_tensor": [], + "shape_input_tensor": [], + "x_type": [], + "start": [], + "end": [] +} + +def calculate_y (x_shape, start, end): + """ + Function to calculate the expected output shape y + """ + rank = len(x_shape) + # Clamp start + if start < 0: + start += rank + if start < 0: + start = 0 + # Clamp end + if end < 0: + end += rank + if end > rank: + end = rank + if start > end: + return [] + else: + return list(x_shape[start:end]) + +""" +Function to generate valid shape arguments +""" +@st.composite +@settings() +def valid_shape_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + + # Input type consistency + all_valid_types = list(shape_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + input_type = draw(st.sampled_from(all_valid_types)) + input_dtype = shape_types.get(inputs_attributes["ONNXRuntime_Provider"])[input_type] + + if np.issubdtype(input_dtype, np.integer): + min_val = np.iinfo(input_dtype).min + max_val = np.iinfo(input_dtype).max + input_strategy = st.integers(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.floating): + min_val = np.finfo(input_dtype).min + max_val = np.finfo(input_dtype).max + input_strategy = st.floats(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.bool_): + input_strategy = st.booleans() + elif np.issubdtype(input_dtype, np.str_): + input_strategy = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + elif input_type == "BFLOAT16": + min_bfloat16 = float(ml_dtypes.finfo(shape_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).min) + max_bfloat16 = float(ml_dtypes.finfo(shape_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).max) + input_strategy = st.floats(min_value=min_bfloat16, max_value=max_bfloat16) + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + rank_input_tensor = draw(st.integers( + min_value=inputs_attributes["min_rank_input"], + max_value=inputs_attributes["max_rank_input"] + )) + + shape_input_tensor = [] + for _ in range(rank_input_tensor): + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_size_input"], + max_value=inputs_attributes["max_dim_size_input"] + )) + shape_input_tensor.append(dim_size) + + if input_type == "BFLOAT16": + temp_tensor = draw(hnp.arrays(dtype=np.float32, shape=shape_input_tensor, elements=input_strategy)) + tf_tensor = tf.cast(tf.constant(temp_tensor), tf.bfloat16) + x = tf_tensor.numpy() + else: + x = draw(hnp.arrays(dtype=input_dtype, shape=shape_input_tensor, elements=input_strategy)) + + #--------------------------------------------------- + # Attribute start + #--------------------------------------------------- + + start = draw(st.integers( + min_value=inputs_attributes["start_min"], + max_value=inputs_attributes["start_max"] + )) + + #--------------------------------------------------- + # Attribute end + #--------------------------------------------------- + + end = draw(st.integers( + min_value=inputs_attributes["end_min"], + max_value=inputs_attributes["end_max"] + )) + + #--------------------------------------------------- + # Output y + #--------------------------------------------------- + y_shape = [len(calculate_y(x.shape, start, end))] + + return x, start, end, y_shape + + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_shape_args()) +def test_shape(args): + x, start, end, y_shape = args + generated_data["rank_input_tensor"].append(len(x.shape)) + generated_data["shape_input_tensor"].append(list(x.shape)) + x_type_key = dtype_to_key.get(x.dtype.type, str(x.dtype)) + generated_data["x_type"].append(x_type_key) + generated_data["start"].append(start) + generated_data["end"].append(end) + y = run_onnx_shape_test(x, start, end, y_shape, inputs_attributes["ONNXRuntime_Provider"]) + check_constraints(x, start, end, y) + + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated by Hypothesis for Shape operation tests", + "min_rank_input": inputs_attributes["min_rank_input"], + "max_rank_input": inputs_attributes["max_rank_input"], + "rank_input_tensor": generated_data["rank_input_tensor"], + "min_dim_size_input": inputs_attributes["min_dim_size_input"], + "max_dim_size_input": inputs_attributes["max_dim_size_input"], + "shape_input_tensor": generated_data["shape_input_tensor"], + "x_type": generated_data["x_type"], + "start_min": inputs_attributes["start_min"], + "start_max": inputs_attributes["start_max"], + "start": generated_data["start"], + "end_min": inputs_attributes["end_min"], + "end_max": inputs_attributes["end_max"], + "end": generated_data["end"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"] + } + + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def run_onnx_shape_test(x, start, end, y_shape, provider): + """ + Function that runs the ONNX Shape operation + """ + x_onnx = helper.make_tensor_value_info('x', helper.np_dtype_to_tensor_dtype(x.dtype), x.shape) + + y_onnx = helper.make_tensor_value_info('y', onnx.TensorProto.INT64, y_shape) + + node_def = helper.make_node( + 'Shape', + inputs=['x'], + outputs=['y'], + start=start, + end=end + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test_shape', + [x_onnx], + [y_onnx], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + if str(x.dtype) == "bfloat16": + # Use ONNX Reference Implementation for bfloat16 + # BFLOAT16 is not supported by ONNX Runtime while using numpy + # An alternative is to use torch tensores and CUDAProvider + sess = onnx.reference.ReferenceEvaluator(onnx_model) + else: + # Use ONNX Runtime for other types + sess = InferenceSession(onnx_model.SerializeToString(), + providers=[provider]) + + y = sess.run(None, {'x': x})[0] + print("x.shape:", x.shape, "start:", start, "end:", end) + print("x.type: ", x.dtype) + print("y shape:", y.shape) + print("y dtype:", y.dtype) + print("y:", y) + return y + +def check_constraints(x, start, end, y): + """ + Function that checks the constraints of the Shape operation + """ + expected_y = calculate_y(x.shape, start, end) + assert list(y) == expected_y, f"Output shape {list(y)} does not match expected shape {expected_y}, x shape: {x.shape}, start: {start}, end: {end}" \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/shape.ipynb b/safety-related-profile/sonnx/ops/spec/tests/shape/shape.ipynb new file mode 100644 index 00000000..1a5b294c --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/shape/shape.ipynb @@ -0,0 +1,328 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "245013fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "shape_output_name = \"Y\"\n", + "\n", + "# Create the ONNX model with Shape operator\n", + "def create_shape_model(input_rank, start, end, dtype):\n", + "\n", + " #Create input tensor\n", + " input1 = onnx.helper.make_tensor_value_info(input_name, dtype, input_rank)\n", + "\n", + " # Create output tensor (final result after shape operation)\n", + " shape_output = onnx.helper.make_tensor_value_info(shape_output_name, onnx.TensorProto.INT64, [None])\n", + "\n", + " # Define shape node\n", + " shape_node = onnx.helper.make_node(\n", + " \"Shape\",\n", + " inputs=[input_name],\n", + " outputs=[shape_output_name],\n", + " start=start,\n", + " end=end\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [shape_node],\n", + " \"Shape\",\n", + " [input1],\n", + " [shape_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)]) # Explicitly set opset to 22\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"shape.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"shape.onnx\")\n", + " return session\n", + "\n", + "def do_shapes(x, session):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(\"Shape of input tensor:\", x.shape)\n", + " print(f\"X={x_f}\")\n", + " print(\"Shape of output tensor:\", output[0].shape)\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "fda05307", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (3,)\n", + "Result = [2,3,4]\n" + ] + } + ], + "source": [ + "# Case N1: 3-rank tensor (int32), start = 0, end = 3\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = 0\n", + "end = 3\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "07d55e8b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1,)\n", + "Result = [3]\n" + ] + } + ], + "source": [ + "# Case N2: 3-rank tensor (int32), start = 1, end = 2\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = 1\n", + "end = 2\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "648f16c5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (0,)\n", + "Result = []\n" + ] + } + ], + "source": [ + "# Case N3: 3-rank tensor (int32), start = 2, end = 2\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = 2\n", + "end = 2\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "713bfc90", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (2,)\n", + "Result = [2,3]\n" + ] + } + ], + "source": [ + "# Case N4: 3-rank tensor (int32), start = -500, end = 2\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = -500\n", + "end = 2\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c3910fc2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (3,)\n", + "Result = [2,3,4]\n" + ] + } + ], + "source": [ + "# Case N5: 3-rank tensor (int32), start = 0, end = 1000\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = 0\n", + "end = 1000\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a42985b4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (0,)\n", + "Result = []\n" + ] + } + ], + "source": [ + "# Case N6: 3-rank tensor (int32), start = -500, end = -400\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = -500\n", + "end = -400\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "17a051a9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (0,)\n", + "Result = []\n" + ] + } + ], + "source": [ + "# Case N7: 3-rank tensor (int32), start = 400, end = 500\n", + "onnx_type = onnx.TensorProto.INT32\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "input_shape = x.shape \n", + "start = 400\n", + "end = 500\n", + "session = create_shape_model([None,None,None], start, end, onnx_type)\n", + "do_shapes(x, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/shape/shape.onnx b/safety-related-profile/sonnx/ops/spec/tests/shape/shape.onnx new file mode 100644 index 00000000..697dcf41 Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/shape/shape.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/sigmoid/README.md b/safety-related-profile/sonnx/ops/spec/tests/sigmoid/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/sigmoid/sigmoid_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/sigmoid/sigmoid_doc.ipynb new file mode 100644 index 00000000..69d20edd --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/sigmoid/sigmoid_doc.ipynb @@ -0,0 +1,215 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "Result: sigmoid(X)=[[0.73105860,0.88079709], [0.95257413,0.98201376]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: sigmoid(X)=[[0.73105860], [0.73105860]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: sigmoid(X)=[[0.50000000], [0.50000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: sigmoid(X)=[[0.26894143], [0.26894143]]\n", + "X=[[inf], [inf]]\n", + "Result: sigmoid(X)=[[1.00000000], [1.00000000]]\n", + "X=[[-inf], [-inf]]\n", + "Result: sigmoid(X)=[[0.00000000], [0.00000000]]\n", + "X=[[nan], [nan]]\n", + "Result: sigmoid(X)=[[nan], [nan]]\n", + "\n", + "## Example 1\n", + "X=[[ 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: sigmoid(X)=[[0.50000000,0.73105860,0.26894143]]\n", + "\n", + "## Example 2\n", + "X=[[-2.00000000, 0.00000000], [ 1.00000000, 2.00000000], [-4.00000000, 4.00000000]]\n", + "Result: sigmoid(X)=[[0.11920291,0.50000000], [0.73105860,0.88079709], [0.01798624,0.98201376]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan,-inf]]\n", + "Result: sigmoid(X)=[[1.00000000, nan,0.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "sigmoid_output_name = \"SigmoidOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after sigmoid operation)\n", + "sigmoid_output = onnx.helper.make_tensor_value_info(sigmoid_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define Sigmoid node: Y = Sigmoid(X)\n", + "sigmoid_node = onnx.helper.make_node(\"Sigmoid\", [input_name], [sigmoid_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[sigmoid_node],\n", + " name=\"Sigmoid\",\n", + " inputs=[input_tensor],\n", + " outputs=[sigmoid_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Sigmoid available since early opsets, 13 is safe)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"sigmoid.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"sigmoid.onnx\")\n", + "\n", + "def do_sigmoid(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: sigmoid(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],\n", + " [3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_sigmoid(x)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_sigmoid(x_np)\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [0, 1, -1]\n", + "x_example_1 = np.array([[0.0, 1.0, -1.0]], dtype=np.float32)\n", + "do_sigmoid(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[ -2 0 ]\n", + "# [ 1 2 ]\n", + "# [ -4 4 ]]\n", + "x_example_2 = np.array([\n", + " [-2.0, 0.0],\n", + " [1.0, 2.0],\n", + " [-4.0, 4.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_sigmoid(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# X =\n", + "# [[ inf nan -inf]\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\")]\n", + "], dtype=np.float32)\n", + "\n", + "do_sigmoid(x_example_3)" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python (.venv)", + "language": "python", + "name": "venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/slice/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/slice/check_edges.py new file mode 100644 index 00000000..152a01b4 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/slice/check_edges.py @@ -0,0 +1,180 @@ +""" +This file checks edge cases in the generated data for the Clip operation in ONNX. +""" +import json +import numpy as np + +index_types = { + "INT32": np.int32, + "INT64": np.int64, +} + +def check_edges(data): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r", encoding="utf-8") as f: + data = json.load(f) + + + check_rank_input_tensor = check_individual_variables( + "rank_input_tensor", data["rank_input_tensor"], data["min_rank_input"], data["max_rank_input"] + ) + shape_size_input_tensor = data["shape_input_tensor"] + shape_size_input_tensor_flatten = [item for sublist in shape_size_input_tensor for item in sublist] + check_shape_size_input_tensor = check_individual_variables( + "shape_size_input_tensor", shape_size_input_tensor_flatten, + data["min_dim_size_input"], data["max_dim_size_input"]) + + start_tensor = data["s_tensor"] + start_tensor_flatten = [item for sublist in start_tensor for item in sublist] + check_start_tensor = check_individual_variables( + "start_tensor", start_tensor_flatten, + -data["max_dim_size_input"], data["max_dim_size_input"] - 1 + ) + + end_tensor = data["e_tensor"] + end_tensor_flatten = [item for sublist in end_tensor for item in sublist] + check_end_tensor = check_individual_variables( + "end_tensor", end_tensor_flatten, + -data["max_dim_size_input"] - 1, data["max_dim_size_input"] + ) + + check_start_tensor_input = check_start_input_tensor( + "start_tensor", start_tensor_flatten, shape_size_input_tensor_flatten + ) + + steps = data["k_tensor"] + steps_flatten = [item for sublist in steps for item in sublist[0]] + check_end_tensor_input = check_end_input_tensor( + "end_tensor", end_tensor_flatten, shape_size_input_tensor_flatten, steps_flatten + ) + + check_steps_tensor = check_bondaries_tensors("steps_tensor", data["k_tensor"]) + + return all([check_rank_input_tensor, check_shape_size_input_tensor, check_start_tensor, check_end_tensor, check_start_tensor_input, check_end_tensor_input, check_steps_tensor]) + + +def check_individual_variables(variable_name, variable_analysis, min_value, max_value): + """ + Check individual variable analysis + """ + corner_cases = [min_value, max_value] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + has_mean_case_mean = any(d for d in variable_analysis if d > min_value and d < max_value) + has_out_of_bounds = not any(d for d in variable_analysis if d < min_value or d > max_value) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(" - Missing mean case") + if not has_out_of_bounds: + print(" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + +def check_start_input_tensor(variable_name, variable_analysis, input_tensor): + """ + Check start tensor edge cases on input tensor + """ + min_edge_case = set() + max_edge_case = set() + for i in range(len(input_tensor)): + min_val = -input_tensor[i] + max_val = input_tensor[i] - 1 + if min_val == variable_analysis[i]: + min_edge_case.add(variable_analysis[i]) + if max_val == variable_analysis[i]: + max_edge_case.add(variable_analysis[i]) + if len(min_edge_case) < 1 and len(max_edge_case) < 1: + print(f"Edge case analysis: {variable_name}") + print(f" - Found only {len(min_edge_case)} min edge cases and {len(max_edge_case)} max edge cases for input tensor entries") + return False + else: + print(f"Edge case analysis: {variable_name}") + return True + +def check_end_input_tensor(variable_name, variable_analysis, input_tensor, steps): + """ + Check end tensor edge cases on input tensor + """ + min_edge_case = set() + max_edge_case = set() + for i in range(len(input_tensor)): + if steps[i] > 0: + min_val = -input_tensor[i] + max_val = input_tensor[i] + else: + min_val = -input_tensor[i] - 1 + max_val = input_tensor[i] - 1 + if min_val == variable_analysis[i]: + min_edge_case.add(variable_analysis[i]) + if max_val == variable_analysis[i]: + max_edge_case.add(variable_analysis[i]) + if len(min_edge_case) < 1 and len(max_edge_case) < 1: + print(f"Edge case analysis: {variable_name}") + print(f" - Found only {len(min_edge_case)} min edge cases and {len(max_edge_case)} max edge cases for input tensor entries") + return False + else: + print(f"Edge case analysis: {variable_name}") + return True + + +def check_bondaries_tensors(variable_name, variable_analysis): + """ + Check interval tensors + """ + minimum_dtypes = get_minimum_dtype() + maximum_dtypes = get_maximum_dtype() + separated_analysis = separate_by_dtype(variable_analysis) + checks = [] + for key, value in separated_analysis.items(): + checks.append(check_individual_variables( + f"{variable_name} - {key}", value, minimum_dtypes[key], maximum_dtypes[key])) + return all(checks) + +def separate_by_dtype(variable_analysis): + """ + Separate the analysis by dtype + """ + separated_analysis = {np.dtype(dtype).name: [] for dtype in index_types.values()} + for item in variable_analysis: + dtype = str(item[1]) + if dtype.startswith("=1.22 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/jcm-machado/venv/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/jcm-machado/venv/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/jcm-machado/venv/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + " \n", + "# Define input and output tensor names\n", + "x_tensor_name = \"X\"\n", + "s_tensor_name = \"S\"\n", + "e_tensor_name = \"E\"\n", + "a_tensor_name = \"A\"\n", + "k_tensor_name = \"K\"\n", + "slice_output_name = \"Y\"\n", + " \n", + "# Create the ONNX model with Slice operator\n", + "def create_slice_model(input_rank, dtype_x, dtype_s, output_shape):\n", + " \n", + " #Create inputs tensors\n", + " x_onnx = onnx.helper.make_tensor_value_info(x_tensor_name, dtype_x, input_rank)\n", + " s_onnx = onnx.helper.make_tensor_value_info(s_tensor_name, dtype_s, [len(input_rank)])\n", + " e_onnx = onnx.helper.make_tensor_value_info(e_tensor_name, dtype_s, [len(input_rank)])\n", + " a_onnx = onnx.helper.make_tensor_value_info(a_tensor_name, dtype_s, [len(input_rank)])\n", + " k_onnx = onnx.helper.make_tensor_value_info(k_tensor_name, dtype_s, [len(input_rank)])\n", + " \n", + " # Create output tensor (final result after slice operation)\n", + " slice_output = onnx.helper.make_tensor_value_info(slice_output_name, dtype_x, output_shape)\n", + " \n", + " # Define slice node\n", + " slice_node = onnx.helper.make_node(\n", + " \"Slice\",\n", + " inputs=[x_tensor_name, s_tensor_name, e_tensor_name, a_tensor_name, k_tensor_name],\n", + " outputs=[slice_output_name]\n", + " )\n", + " \n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [slice_node],\n", + " \"Slice\",\n", + " [x_onnx, s_onnx, e_onnx, a_onnx, k_onnx],\n", + " [slice_output],\n", + " )\n", + " \n", + " onnx_model = onnx.helper.make_model(graph_def)\n", + " \n", + " #Let's freeze the opset.\n", + " del onnx_model.opset_import[:]\n", + " opset = onnx_model.opset_import.add()\n", + " opset.domain = ''\n", + " opset.version = 13\n", + " onnx_model.ir_version = 10\n", + " \n", + " # Verify the model\n", + " onnx.checker.check_model(onnx_model)\n", + " \n", + " # Save the model\n", + " onnx.save(onnx_model, \"slice.onnx\")\n", + " \n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"slice.onnx\")\n", + " return session\n", + " \n", + "def do_slice(x, s, e, a, k, session):\n", + " # Run inference\n", + " output = session.run(None, {x_tensor_name: x, s_tensor_name: s, e_tensor_name: e, a_tensor_name: a, k_tensor_name: k})\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " \n", + " # Display results\n", + " print(\"Shape of input tensor:\", x.shape)\n", + " print(f\"X={x_f}\")\n", + " print(\"Shape of output tensor:\", output[0].shape)\n", + " print(f\"Result = {y_f}\")\n", + " \n", + " \n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a6b3ce33", + "metadata": {}, + "outputs": [], + "source": [ + "def output_shape(input_shape, starts, ends, axes, steps):\n", + " #Normalize axes\n", + " for i in range(len(axes)):\n", + " if axes[i] < 0:\n", + " axes[i] += len(input_shape)\n", + " #Normalize starts and ends\n", + " for i in range(len(starts)):\n", + " if starts[i] < 0:\n", + " starts[i] += input_shape[axes[i]]\n", + " if ends[i] < 0:\n", + " ends[i] += input_shape[axes[i]]\n", + "\n", + " #Compute output shape\n", + " rank_input_tensor = len(input_shape)\n", + " output_shape = [0] * rank_input_tensor\n", + " # Y [C1] -> X [C1]\n", + " for i in range(rank_input_tensor):\n", + " space_i = ends[i] - starts[i]\n", + " k_val = steps[i]\n", + " f = 0 if space_i % k_val == 0 else 1\n", + " dY_i = (space_i // k_val) + f\n", + " #TODO Informal spec mal\n", + " output_shape[axes[i]] = int(dY_i)\n", + " print(\"Expected output shape:\", output_shape)\n", + " return output_shape" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fda05307", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Expected output shape: [4, 3]\n", + "Shape of input tensor: (5, 6)\n", + "X=[[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10,11], [12,13,14,15,16,17], [18,19,20,21,22,23], [24,25,26,27,28,29]]\n", + "Shape of output tensor: (4, 3)\n", + "Result = [[ 1, 3, 5], [ 7, 9,11], [13,15,17], [19,21,23]]\n" + ] + } + ], + "source": [ + "# Case N1: 2-rank tensor (int32), start=[0,1], end=[4,6], axes=[0,1], steps=[1,2]\n", + "input_type = onnx.TensorProto.INT32\n", + "x = np.arange(30).reshape(5,6).astype(np.int32)\n", + "s_type = onnx.TensorProto.INT32\n", + "s_tensor = np.array([0,1]).astype(np.int32)\n", + "e_tensor = np.array([4,6]).astype(np.int32)\n", + "a_tensor = np.array([0,1]).astype(np.int32)\n", + "k_tensor = np.array([1,2]).astype(np.int32)\n", + "output_shape_ = output_shape(x.shape, s_tensor, e_tensor, a_tensor, k_tensor)\n", + "session = create_slice_model(x.shape, input_type, s_type, output_shape_)\n", + "do_slice(x, s_tensor, e_tensor, a_tensor, k_tensor, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "21fb795e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "input shape (5, 6)\n", + "Expected output shape: [5, 6]\n", + "Shape of input tensor: (5, 6)\n", + "X=[[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10,11], [12,13,14,15,16,17], [18,19,20,21,22,23], [24,25,26,27,28,29]]\n", + "Shape of output tensor: (5, 6)\n", + "Result = [[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10,11], [12,13,14,15,16,17], [18,19,20,21,22,23], [24,25,26,27,28,29]]\n" + ] + } + ], + "source": [ + "# Case N2: 2-rank tensor (int32), axes=[0,1], start=[0,0], end=[5,6], steps=[1,1]\n", + "# Complete tensor\n", + "input_type = onnx.TensorProto.INT32\n", + "x = np.arange(30).reshape(5,6).astype(np.int32)\n", + "s_type = onnx.TensorProto.INT32\n", + "s_tensor = np.array([0, 0]).astype(np.int32)\n", + "e_tensor = np.array([5, 6]).astype(np.int32)\n", + "a_tensor = np.array([0, 1]).astype(np.int32)\n", + "k_tensor = np.array([1, 1]).astype(np.int32)\n", + "print(\"input shape\", x.shape)\n", + "output_shape_ = output_shape(x.shape, s_tensor, e_tensor, a_tensor, k_tensor)\n", + "session = create_slice_model(x.shape, input_type, s_type, output_shape_)\n", + "do_slice(x, s_tensor, e_tensor, a_tensor, k_tensor, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "566043ba", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "input shape (5, 6)\n", + "Expected output shape: [3, 2]\n", + "Shape of input tensor: (5, 6)\n", + "X=[[ 0, 1, 2, 3, 4, 5], [ 6, 7, 8, 9,10,11], [12,13,14,15,16,17], [18,19,20,21,22,23], [24,25,26,27,28,29]]\n", + "Shape of output tensor: (3, 2)\n", + "Result = [[ 5, 3], [11, 9], [17,15]]\n" + ] + } + ], + "source": [ + "# Case N3: 2-rank tensor (int32), axes=[0,1], start=[-5,-1], end=[3,2], steps=[1,-2]\n", + "input_type = onnx.TensorProto.INT32\n", + "x = np.arange(30).reshape(5,6).astype(np.int32)\n", + "s_type = onnx.TensorProto.INT32\n", + "s_tensor = np.array([-5, -1]).astype(np.int32)\n", + "e_tensor = np.array([3, 2]).astype(np.int32)\n", + "a_tensor = np.array([0, 1]).astype(np.int32)\n", + "k_tensor = np.array([1, -2]).astype(np.int32)\n", + "print(\"input shape\", x.shape)\n", + "output_shape_ = output_shape(x.shape, s_tensor, e_tensor, a_tensor, k_tensor)\n", + "session = create_slice_model(x.shape, input_type, s_type, output_shape_)\n", + "do_slice(x, s_tensor, e_tensor, a_tensor, k_tensor, session)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/slice/test_slice.py b/safety-related-profile/sonnx/ops/spec/tests/slice/test_slice.py new file mode 100644 index 00000000..99908364 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/slice/test_slice.py @@ -0,0 +1,411 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Slice operation in ONNX. +""" +import os + +import json +import numpy as np +import ml_dtypes + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st +from hypothesis import assume + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + + +from onnx import helper + +import tensorflow as tf + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes details +""" + +inputs_attributes = { + "min_rank_input": 1, # Minimum rank of the input tensor should be at least 1 (no scalars slice) X [C3] + "max_rank_input": 5, # Adjust as needed + "min_dim_size_input": 1, # Dimension should always be positive (no zero dimensions) + "max_dim_size_input": 10 # Adjust as needed +} + +""" +Slice supported types +""" +slice_types = { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "STRING": np.str_, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 +} + + +slice_index_types = { + "INT32": np.int32, + "INT64": np.int64, +} + +""" +Store generated data +""" +generated_data = { + "rank_input_tensor": [], + "shape_input_tensor": [], + "x_tensor": [], + "a_tensor": [], + "k_tensor": [], + "s_tensor": [], + "e_tensor": [], + "x_type": [], + "index_type": [] +} + +""" +Function to generate valid slice arguments +""" +@st.composite +@settings() +def valid_slice_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + + # X [C3] - Input/Output Types Consistency + all_valid_types = list(slice_types.keys()) + input_type = draw(st.sampled_from(all_valid_types)) + input_dtype = slice_types[input_type] + + if np.issubdtype(input_dtype, np.integer): + min_val = np.iinfo(input_dtype).min + max_val = np.iinfo(input_dtype).max + input_strategy = st.integers(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.floating): + min_val = np.finfo(input_dtype).min + max_val = np.finfo(input_dtype).max + input_strategy = st.floats(min_value=min_val, max_value=max_val) + elif np.issubdtype(input_dtype, np.bool_): + input_strategy = st.booleans() + elif np.issubdtype(input_dtype, np.str_): + input_strategy = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + elif input_type == "BFLOAT16": + min_bfloat16 = float(ml_dtypes.finfo(slice_types["BFLOAT16"]).min) + max_bfloat16 = float(ml_dtypes.finfo(slice_types["BFLOAT16"]).max) + input_strategy = st.floats(min_value=min_bfloat16, max_value=max_bfloat16) + + # Index Types Consistency + # S [C4] , E [C4] -> S [C4], A[C4] -> S [C4], K [C4] -> S [C4] + all_valid_index_types = list(slice_index_types.keys()) + index_type = draw(st.sampled_from(all_valid_index_types)) + index_dtype = slice_index_types[index_type] + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + rank_input_tensor = draw(st.integers( + min_value=inputs_attributes["min_rank_input"], + max_value=inputs_attributes["max_rank_input"] + )) + + shape_input_tensor = [] + for _ in range(rank_input_tensor): + dim_size = draw(st.integers( + min_value=inputs_attributes["min_dim_size_input"], + max_value=inputs_attributes["max_dim_size_input"] + )) + shape_input_tensor.append(dim_size) + + if input_type == "BFLOAT16": + temp_tensor = draw(hnp.arrays(dtype=np.float32, shape=shape_input_tensor, elements=input_strategy)) + tf_tensor = tf.cast(tf.constant(temp_tensor), tf.bfloat16) + x = tf_tensor.numpy() + else: + x = draw(hnp.arrays(dtype=input_dtype, shape=shape_input_tensor, elements=input_strategy)) + + #--------------------------------------------------- + # Input A + #--------------------------------------------------- + # A [C1] -> X [C1] + da0 = rank_input_tensor + # A [C2], A [C3] + possible_indices = draw(st.permutations(range(rank_input_tensor)))[:da0] + a = [] + for idx in possible_indices: + make_negative = draw(st.booleans()) + if make_negative: + a.append(idx - rank_input_tensor) + else: + a.append(idx) + #A [C4] + a = np.array(a, dtype=index_dtype) + a_normalized = np.where(a < 0, a + rank_input_tensor, a) + + #--------------------------------------------------- + # Input K + #--------------------------------------------------- + # K [C1] -> X [C1] + dk0 = rank_input_tensor + # K [C4] + k = np.empty(dk0, dtype=index_dtype) + # K [C2] + k_strategy = st.one_of( + st.integers(min_value=np.iinfo(index_dtype).min, max_value=-1), + st.integers(min_value=1, max_value=np.iinfo(index_dtype).max) + ) + for i in range(rank_input_tensor): + k[i] = draw(k_strategy) + + #--------------------------------------------------- + # Input S + #--------------------------------------------------- + # S [1] -> X [C1] + ds0 = rank_input_tensor + # S [C4] + s = np.empty(ds0, dtype=index_dtype) + # S [C2] + for i in range(rank_input_tensor): + min_val = -shape_input_tensor[a_normalized[i]] + max_val = shape_input_tensor[a_normalized[i]] - 1 + s_strategy = st.integers(min_value=min_val, max_value=max_val) + s[i] = draw(s_strategy) + s_normalized = np.empty(ds0, dtype=index_dtype) + for i in range(len(s)): + if s[i] < 0: + s_normalized[i] = s[i] + shape_input_tensor[a_normalized[i]] + else: + s_normalized[i] = s[i] + + #--------------------------------------------------- + # Input E + #--------------------------------------------------- + # E [1] -> X [C1] + de0 = rank_input_tensor + # E [C4] + e = np.empty(de0, dtype=index_dtype) + # E [C2] + for i in range(rank_input_tensor): + # E [C3] -> S [C3], K [C3] -> S [C3], S [C3] + if k[i] > 0: + min_val = s[i] + max_val = shape_input_tensor[a_normalized[i]] + else: + min_val = -shape_input_tensor[a_normalized[i]] - 1 + max_val = s[i] + s_strategy = st.integers(min_value=min_val, max_value=max_val) + e[i] = draw(s_strategy) + e_normalized = np.empty(de0, dtype=index_dtype) + for i in range(len(e)): + if e[i] < 0: + e_normalized[i] = e[i] + shape_input_tensor[a_normalized[i]] + else: + e_normalized[i] = e[i] + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + # Y [C1] -> X [C1] + output_shape = [0] * rank_input_tensor + # Y [C2] + for i in range(rank_input_tensor): + space_i = e_normalized[i] - s_normalized[i] + k_val = k[i] + f = 0 if space_i % k_val == 0 else 1 + dY_i = (space_i // k_val) + f + assume(dY_i > 0) + output_shape[a_normalized[i]] = int(dY_i) + return x, a, k, s, e, output_shape, s_normalized, a_normalized, e_normalized + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_slice_args()) +def test_slice(args): + x, a, k, s, e, y_shape, s_normalized, a_normalized, e_normalized = args + generated_data["rank_input_tensor"].append(len(x.shape)) + generated_data["shape_input_tensor"].append(list(x.shape)) + generated_data["x_tensor"].append(x.tolist()) + generated_data["a_tensor"].append(a.tolist()) + generated_data["k_tensor"].append(k) + generated_data["s_tensor"].append(s.tolist()) + generated_data["e_tensor"].append(e.tolist()) + generated_data["x_type"].append(str(x.dtype)) + generated_data["index_type"].append(str(a.dtype)) + + y = run_onnx_slice(x, a, k, s, e, y_shape) + check_constraints(x, y, s_normalized, k, a_normalized, e_normalized, s, a, e, y_shape) + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated by Hypothesis for Slice operation tests", + "min_rank_input": inputs_attributes["min_rank_input"], + "max_rank_input": inputs_attributes["max_rank_input"], + "rank_input_tensor": generated_data["rank_input_tensor"], + "min_dim_size_input": inputs_attributes["min_dim_size_input"], + "max_dim_size_input": inputs_attributes["max_dim_size_input"], + "shape_input_tensor": generated_data["shape_input_tensor"], + "x_tensor": generated_data["x_tensor"], + "a_tensor": generated_data["a_tensor"], + "k_tensor": generated_data["k_tensor"], + "s_tensor": generated_data["s_tensor"], + "e_tensor": generated_data["e_tensor"], + "x_type": generated_data["x_type"], + "index_type": generated_data["index_type"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + data["k_tensor"] = [(arr.tolist(), str(arr.dtype)) for arr in data["k_tensor"]] + json.dump(data, f, indent=4) + +def run_onnx_slice(x, a, k, s, e, y_shape): + """ + Function that runs the ONNX Slice operation + """ + x_onnx = helper.make_tensor_value_info('x', helper.np_dtype_to_tensor_dtype(x.dtype), x.shape) + a_onnx = helper.make_tensor_value_info('a', helper.np_dtype_to_tensor_dtype(a.dtype), a.shape) + k_onnx = helper.make_tensor_value_info('k', helper.np_dtype_to_tensor_dtype(k.dtype), k.shape) + s_onnx = helper.make_tensor_value_info('s', helper.np_dtype_to_tensor_dtype(s.dtype), s.shape) + e_onnx = helper.make_tensor_value_info('e', helper.np_dtype_to_tensor_dtype(e.dtype), e.shape) + y_onnx = helper.make_tensor_value_info('y', helper.np_dtype_to_tensor_dtype(x.dtype), y_shape) + + node_def = helper.make_node( + 'Slice', + inputs=['x', 's', 'e', 'a', 'k'], + outputs=['y'], + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-clip', + [x_onnx, s_onnx, e_onnx, a_onnx, k_onnx], + [y_onnx], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 13 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + if str(x.dtype) == "bfloat16": + # Use ONNX Reference Implementation for bfloat16 + # BFLOAT16 is not supported by ONNX Runtime while using numpy + # An alternative is to use torch tensores and CUDAProvider + sess = onnx.reference.ReferenceEvaluator(onnx_model) + else: + # Use ONNX Runtime for other types + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + y = sess.run(None, {'x': x, 's': s, 'e': e, 'a': a, 'k': k})[0] + print("y shape:", y.shape) + print("y values:", y) + print("y data type:", y.dtype) + return y + +def check_output_input(x, y, start, step, axis): + indices = np.ndindex(y.shape) + + for out_idx in indices: + real_index = [] + for dim in range(len(out_idx)): + axis_index = axis.tolist().index(dim) + real_index.append(axis[axis_index]) + + for out_idx in indices: + in_idx = tuple(start[real_index[dim]] + out_idx[dim] * step[real_index[dim]] for dim in range(len(out_idx))) + if x[in_idx] != y[out_idx]: + print(f"Mismatch at output index {out_idx}: expected {x[in_idx]}, got {y[out_idx]}") + return False + + return True + +def check_constraints(x, y, s_normalized, k, a_normalized, e_normalized, s, a, e, y_shape): + """ + Check constraints for generated data + """ + # X Constraints + # X [C1], S [C1] -> X [C1], E [C1] -> X [C1], A [C1] -> X [C1], K [C1] -> X [C1] + assert len(x.shape) == len(s_normalized) == len(k) == len(a_normalized) == len(e_normalized) + # X [C2] + assert len(x.shape) == len(y.shape) + # X [C3] + assert len(x.shape) >= 1 + # X [C4] + if np.issubdtype(x.dtype, np.str_) or np.issubdtype(x.dtype, np.object_): + assert np.issubdtype(y.dtype, np.str_) or np.issubdtype(y.dtype, np.object_) + else: + assert x.dtype == y.dtype + + # S Constraints + # S [C2] + for i in range(len(s)): + assert -x.shape[a_normalized[i]] <= s[i] <= x.shape[a_normalized[i]] - 1 + # S [C3], E [C3] -> S [C3], K [C3] -> S [C3] + for i in range(len(s_normalized)): + if k[i] > 0: + assert s_normalized[i] <= e_normalized[i] <= x.shape[a_normalized[i]] + else: + assert s_normalized[i] >= e_normalized[i] + + # S [C4], E [C4] -> S [C4], K [C4] -> S [C4], A [C4] -> S [C4] + assert s.dtype == k.dtype == a.dtype == e.dtype + + # S Constraints + # S [C2] + for i in range(len(s)): + if k[i] > 0: + assert -x.shape[a_normalized[i]] <= s[i] <= x.shape[a_normalized[i]] + else: + assert -x.shape[a_normalized[i]] - 1 <= s[i] <= x.shape[a_normalized[i]] - 1 + + # A Constraints + # A [C2] + for i in range(len(a)): + assert -len(x.shape) <= a[i] <= len(x.shape) - 1 + # A [C3] + axis_unique = set() + for i in range(len(a_normalized)): + assert a_normalized[i] not in axis_unique + axis_unique.add(a_normalized[i]) + + # K Constraints + # K [C2] + for i in range(len(k)): + assert k[i] != 0 + + # Output Y Constraints + # Y [C2] + assert list(y.shape) == y_shape + + #Y [C3] + assert check_output_input(x, y, s_normalized, k, a_normalized) diff --git a/safety-related-profile/sonnx/ops/spec/tests/softmax/README.md b/safety-related-profile/sonnx/ops/spec/tests/softmax/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/softmax/softmax_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/softmax/softmax_doc.ipynb new file mode 100644 index 00000000..a4980949 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/softmax/softmax_doc.ipynb @@ -0,0 +1,436 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "## Nominal Cases\n", + "\n", + "X (axis=1) = [[ 9.50000000,35.70000076]]\n", + "Softmax(X) = [[4.18296517e-12,1.00000000e+00]]\n", + "\n", + "X (axis=0) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000,6.00000000]]\n", + "Softmax(X) = [[0.04742587,0.04742587,0.04742587], [0.95257413,0.95257413,0.95257413]]\n", + "\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000,6.00000000]]\n", + "Softmax(X) = [[0.09003057,0.24472848,0.66524094], [0.09003057,0.24472848,0.66524094]]\n", + "\n", + "## Special Value Cases\n", + "\n", + "X (axis=0) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, inf]]\n", + "Softmax(X) = [[0.04742587,0.04742587, nan], [0.95257413,0.95257413, nan]]\n", + "\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, inf]]\n", + "Softmax(X) = [[0.09003057,0.24472848,0.66524094], [ nan, nan, nan]]\n", + "\n", + "X (axis=0) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, -inf]]\n", + "Softmax(X) = [[0.04742587,0.04742587,1.00000000], [0.95257413,0.95257413,0.00000000]]\n", + "\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, -inf]]\n", + "Softmax(X) = [[0.09003057,0.24472848,0.66524094], [0.26894143,0.73105860,0.00000000]]\n", + "\n", + "X (axis=0) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, nan]]\n", + "Softmax(X) = [[0.04742587,0.04742587, nan], [0.95257413,0.95257413, nan]]\n", + "\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000], [4.00000000,5.00000000, nan]]\n", + "Softmax(X) = [[0.09003057,0.24472848,0.66524094], [ nan, nan, nan]]\n", + "\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "softmax_output_name = \"SoftmaxOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after softmax operation)\n", + "softmax_output = onnx.helper.make_tensor_value_info(softmax_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "def create_softmax_model(axis=1):\n", + " # Define Softmax node: Y = Softmax(X) with axis\n", + " softmax_node = onnx.helper.make_node(\"Softmax\", [input_name], [softmax_output_name], axis=axis)\n", + " \n", + " # Create the ONNX graph\n", + " graph = onnx.helper.make_graph(\n", + " nodes=[softmax_node],\n", + " name=f\"Softmax_axis_{axis}\",\n", + " inputs=[input_tensor],\n", + " outputs=[softmax_output]\n", + " )\n", + "\n", + " # Create the ONNX model (Softmax opset 13)\n", + " model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)])\n", + " onnx.checker.check_model(model)\n", + " onnx.save(model, f\"softmax_axis_{axis}.onnx\")\n", + " return ort.InferenceSession(f\"softmax_axis_{axis}.onnx\")\n", + "\n", + "# Create sessions for axis=0 and axis=1\n", + "session_axis0 = create_softmax_model(axis=0)\n", + "session_axis1 = create_softmax_model(axis=1)\n", + "\n", + "def do_softmax(x, axis=1):\n", + " session = session_axis0 if axis==0 else session_axis1\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " print(f\"X (axis={axis}) = {x_f}\")\n", + " print(f\"Softmax(X) = {o_f}\\n\")\n", + "\n", + "\n", + "np.set_printoptions(precision=8, floatmode='fixed')\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"## Nominal Cases\\n\")\n", + "\n", + "# Case 1: 1x2 vector\n", + "x = np.array([[9.5, 35.7]], dtype=np.float32)\n", + "do_softmax(x, axis=1)\n", + "\n", + "# Case 2: 2x3 matrix\n", + "x = np.array([[1.0, 2.0, 3.0],\n", + " [4.0, 5.0, 6.0]], dtype=np.float32)\n", + "do_softmax(x, axis=0)\n", + "do_softmax(x, axis=1)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Special values: +inf, -inf, NaN\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"## Special Value Cases\\n\")\n", + "\n", + "# Case +inf\n", + "x = np.array([[1, 2, 3],\n", + " [4, 5, np.inf]], dtype=np.float32)\n", + "do_softmax(x, axis=0)\n", + "do_softmax(x, axis=1)\n", + "\n", + "# Case -inf\n", + "x = np.array([[1, 2, 3],\n", + " [4, 5, -np.inf]], dtype=np.float32)\n", + "do_softmax(x, axis=0)\n", + "do_softmax(x, axis=1)\n", + "\n", + "# Case NaN\n", + "x = np.array([[1, 2, 3],\n", + " [4, 5, np.nan]], dtype=np.float32)\n", + "do_softmax(x, axis=0)\n", + "do_softmax(x, axis=1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "## Test 6: 3 flottants normaux + +inf\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, inf]]\n", + "Softmax(X) = [[nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 7: 3 flottants normaux + -inf\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, -inf]]\n", + "Softmax(X) = [[0.09003057,0.24472848,0.66524094,0.00000000]]\n", + "\n", + "\n", + "## Test 8: 3 flottants normaux + NaN\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, nan]]\n", + "Softmax(X) = [[nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 9: 3 flottants égaux à -0\n", + "X (axis=1) = [[-0.00000000,-0.00000000,-0.00000000]]\n", + "Softmax(X) = [[0.33333334,0.33333334,0.33333334]]\n", + "\n", + "\n", + "## Test 10: 3 flottants -0, 0, 0\n", + "X (axis=1) = [[-0.00000000, 0.00000000, 0.00000000]]\n", + "Softmax(X) = [[0.33333334,0.33333334,0.33333334]]\n", + "\n", + "\n", + "## Test 11: 3 flottants normaux + +inf et -inf\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, inf, -inf]]\n", + "Softmax(X) = [[nan,nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 12: 3 flottants normaux + +inf et NaN\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, inf, nan]]\n", + "Softmax(X) = [[nan,nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 13: 3 flottants normaux + +inf et +0\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, inf,0.00000000]]\n", + "Softmax(X) = [[nan,nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 14: 3 flottants normaux + +inf et -0\n", + "X (axis=1) = [[ 1.00000000, 2.00000000, 3.00000000, inf,-0.00000000]]\n", + "Softmax(X) = [[nan,nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 15: 3 flottants normaux + -inf et NaN\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, -inf, nan]]\n", + "Softmax(X) = [[nan,nan,nan,nan,nan]]\n", + "\n", + "\n", + "## Test 16: 3 flottants normaux + -inf et +0\n", + "X (axis=1) = [[1.00000000,2.00000000,3.00000000, -inf,0.00000000]]\n", + "Softmax(X) = [[0.08714432,0.23688284,0.64391428,0.00000000,0.03205860]]\n", + "\n", + "\n", + "## Test 17: 3 flottants normaux + -inf et -0\n", + "X (axis=1) = [[ 1.00000000, 2.00000000, 3.00000000, -inf,-0.00000000]]\n", + "Softmax(X) = [[0.08714432,0.23688284,0.64391428,0.00000000,0.03205860]]\n", + "\n", + "\n", + "## Test 18: 3 flottants normaux <0 + -0 + +0\n", + "X (axis=1) = [[-1.00000000,-2.00000000,-3.00000000,-0.00000000, 0.00000000]]\n", + "Softmax(X) = [[0.14409682,0.05301026,0.01950138,0.39169577,0.39169577]]\n", + "\n", + "\n", + "## Test 19: 3 flottants normaux >0 + -0 + +0\n", + "X (axis=1) = [[ 1.00000000, 2.00000000, 3.00000000,-0.00000000, 0.00000000]]\n", + "Softmax(X) = [[0.08443738,0.22952460,0.62391251,0.03106277,0.03106277]]\n", + "\n", + "\n", + "## Test 20: 1 flottant >0, 1 flottant <0, -0, +0, 2\n", + "X (axis=1) = [[ 1.00000000,-1.00000000,-0.00000000, 0.00000000, 2.00000000]]\n", + "Softmax(X) = [[0.21789455,0.02948882,0.08015893,0.08015893,0.59229881]]\n", + "\n" + ] + } + ], + "source": [ + "#--------------------------------------------------------------------\n", + "# Additional special cases (tests 6 to 20)\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Test 6: 3 flottants normaux + +inf\")\n", + "x_test_6 = np.array([[1.0, 2.0, 3.0, np.inf]], dtype=np.float32)\n", + "do_softmax(x_test_6, axis=1)\n", + "\n", + "print(\"\\n## Test 7: 3 flottants normaux + -inf\")\n", + "x_test_7 = np.array([[1.0, 2.0, 3.0, -np.inf]], dtype=np.float32)\n", + "do_softmax(x_test_7, axis=1)\n", + "\n", + "print(\"\\n## Test 8: 3 flottants normaux + NaN\")\n", + "x_test_8 = np.array([[1.0, 2.0, 3.0, np.nan]], dtype=np.float32)\n", + "do_softmax(x_test_8, axis=1)\n", + "\n", + "print(\"\\n## Test 9: 3 flottants égaux à -0\")\n", + "x_test_9 = np.array([[-0.0, -0.0, -0.0]], dtype=np.float32)\n", + "do_softmax(x_test_9, axis=1)\n", + "\n", + "print(\"\\n## Test 10: 3 flottants -0, 0, 0\")\n", + "x_test_10 = np.array([[-0.0, 0.0, 0.0]], dtype=np.float32)\n", + "do_softmax(x_test_10, axis=1)\n", + "\n", + "print(\"\\n## Test 11: 3 flottants normaux + +inf et -inf\")\n", + "x_test_11 = np.array([[1.0, 2.0, 3.0, np.inf, -np.inf]], dtype=np.float32)\n", + "do_softmax(x_test_11, axis=1)\n", + "\n", + "print(\"\\n## Test 12: 3 flottants normaux + +inf et NaN\")\n", + "x_test_12 = np.array([[1.0, 2.0, 3.0, np.inf, np.nan]], dtype=np.float32)\n", + "do_softmax(x_test_12, axis=1)\n", + "\n", + "print(\"\\n## Test 13: 3 flottants normaux + +inf et +0\")\n", + "x_test_13 = np.array([[1.0, 2.0, 3.0, np.inf, 0.0]], dtype=np.float32)\n", + "do_softmax(x_test_13, axis=1)\n", + "\n", + "print(\"\\n## Test 14: 3 flottants normaux + +inf et -0\")\n", + "x_test_14 = np.array([[1.0, 2.0, 3.0, np.inf, -0.0]], dtype=np.float32)\n", + "do_softmax(x_test_14, axis=1)\n", + "\n", + "print(\"\\n## Test 15: 3 flottants normaux + -inf et NaN\")\n", + "x_test_15 = np.array([[1.0, 2.0, 3.0, -np.inf, np.nan]], dtype=np.float32)\n", + "do_softmax(x_test_15, axis=1)\n", + "\n", + "print(\"\\n## Test 16: 3 flottants normaux + -inf et +0\")\n", + "x_test_16 = np.array([[1.0, 2.0, 3.0, -np.inf, 0.0]], dtype=np.float32)\n", + "do_softmax(x_test_16, axis=1)\n", + "\n", + "print(\"\\n## Test 17: 3 flottants normaux + -inf et -0\")\n", + "x_test_17 = np.array([[1.0, 2.0, 3.0, -np.inf, -0.0]], dtype=np.float32)\n", + "do_softmax(x_test_17, axis=1)\n", + "\n", + "print(\"\\n## Test 18: 3 flottants normaux <0 + -0 + +0\")\n", + "x_test_18 = np.array([[-1.0, -2.0, -3.0, -0.0, 0.0]], dtype=np.float32)\n", + "do_softmax(x_test_18, axis=1)\n", + "\n", + "print(\"\\n## Test 19: 3 flottants normaux >0 + -0 + +0\")\n", + "x_test_19 = np.array([[1.0, 2.0, 3.0, -0.0, 0.0]], dtype=np.float32)\n", + "do_softmax(x_test_19, axis=1)\n", + "\n", + "print(\"\\n## Test 20: 1 flottant >0, 1 flottant <0, -0, +0, 2\")\n", + "x_test_20 = np.array([[1.0, -1.0, -0.0, 0.0, 2.0]], dtype=np.float32)\n", + "do_softmax(x_test_20, axis=1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Code for 3D dimension" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X (axis=2) = [[[ 1.00000000, 2.00000000, 3.00000000], [ 4.00000000, 5.00000000, 6.00000000]], [[10.00000000,20.00000000,30.00000000], [40.00000000,50.00000000,60.00000000]]]\n", + "Softmax(X) = [[[9.00305733e-02,2.44728476e-01,6.65240943e-01], [9.00305733e-02,2.44728476e-01,6.65240943e-01]], [[2.06105999e-09,4.53978682e-05,9.99954581e-01], [2.06105999e-09,4.53978682e-05,9.99954581e-01]]]\n", + "\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "softmax_output_name = \"SoftmaxOutput\"\n", + "\n", + "def create_softmax_model(axis=1, rank=2):\n", + " \"\"\"\n", + " Crée un modèle ONNX Softmax pour un tenseur de rang `rank` et un axe `axis`.\n", + " \"\"\"\n", + " # Définition dynamique de la forme d'entrée et de sortie\n", + " shape = [None] * rank\n", + " input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, shape)\n", + " softmax_output = onnx.helper.make_tensor_value_info(softmax_output_name, onnx.TensorProto.FLOAT, shape)\n", + "\n", + " # Crée le nœud Softmax\n", + " softmax_node = onnx.helper.make_node(\n", + " \"Softmax\",\n", + " inputs=[input_name],\n", + " outputs=[softmax_output_name],\n", + " axis=axis\n", + " )\n", + "\n", + " # Crée le graphe ONNX\n", + " graph = onnx.helper.make_graph(\n", + " nodes=[softmax_node],\n", + " name=f\"Softmax_axis_{axis}_rank_{rank}\",\n", + " inputs=[input_tensor],\n", + " outputs=[softmax_output]\n", + " )\n", + "\n", + " # Crée le modèle ONNX (opset 13)\n", + " model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)])\n", + " onnx.checker.check_model(model)\n", + " onnx.save(model, f\"softmax_axis_{axis}_rank_{rank}.onnx\")\n", + " return ort.InferenceSession(f\"softmax_axis_{axis}_rank_{rank}.onnx\")\n", + "\n", + "def do_softmax(x, axis=1):\n", + " \"\"\"\n", + " Effectue le softmax sur le tenseur x le long de l'axe spécifié.\n", + " Crée une session ONNX adaptée au rang du tenseur et à l'axe.\n", + " \"\"\"\n", + " rank = x.ndim\n", + " session = create_softmax_model(axis=axis, rank=rank)\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " print(f\"X (axis={axis}) = {x_f}\")\n", + " print(f\"Softmax(X) = {o_f}\\n\")\n", + "\n", + "# Options d'affichage pour numpy\n", + "np.set_printoptions(precision=8, floatmode='fixed')\n", + "\n", + "# Exemple avec un tenseur 3D\n", + "x3d = np.array([\n", + " [[1, 2, 3], [4, 5, 6]],\n", + " [[10, 20, 30], [40, 50, 60]]\n", + "], dtype=np.float32)\n", + "\n", + "do_softmax(x3d, axis=2) # Softmax sur la dernière dimension" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python (.venv)", + "language": "python", + "name": "venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/sqrt/README.md b/safety-related-profile/sonnx/ops/spec/tests/sqrt/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/sqrt/sqrt_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/sqrt/sqrt_doc.ipynb new file mode 100644 index 00000000..3a50593d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/sqrt/sqrt_doc.ipynb @@ -0,0 +1,215 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "Result: sqrt(X)=[[1.00000000,1.41421354], [1.73205078,2.00000000]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: sqrt(X)=[[1.00000000], [1.00000000]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: sqrt(X)=[[0.00000000], [0.00000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: sqrt(X)=[[nan], [nan]]\n", + "X=[[inf], [inf]]\n", + "Result: sqrt(X)=[[inf], [inf]]\n", + "X=[[-inf], [-inf]]\n", + "Result: sqrt(X)=[[nan], [nan]]\n", + "X=[[nan], [nan]]\n", + "Result: sqrt(X)=[[nan], [nan]]\n", + "\n", + "## Example 1\n", + "X=[[1.00000000,2.00000000,4.00000000]]\n", + "Result: sqrt(X)=[[1.00000000,1.41421354,2.00000000]]\n", + "\n", + "## Example 2\n", + "X=[[2.50000000e-01,2.25000000e+00], [0.00000000e+00,1.00000001e-01], [1.00000000e+01,1.00000000e+03]]\n", + "Result: sqrt(X)=[[ 0.50000000, 1.50000000], [ 0.00000000, 0.31622776], [ 3.16227770,31.62277603]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan, -inf,-0.00000000]]\n", + "Result: sqrt(X)=[[ inf, nan, nan,-0.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "sqrt_output_name = \"SqrtOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Create output tensor (final result after sqrt operation)\n", + "sqrt_output = onnx.helper.make_tensor_value_info(sqrt_output_name, onnx.TensorProto.FLOAT, [None, None])\n", + "\n", + "# Define Sqrt node: Y = Sqrt(X)\n", + "sqrt_node = onnx.helper.make_node(\"Sqrt\", [input_name], [sqrt_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[sqrt_node],\n", + " name=\"Sqrt\",\n", + " inputs=[input_tensor],\n", + " outputs=[sqrt_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Sqrt is available in opset 13)\n", + "model = onnx.helper.make_model(graph, opset_imports=[onnx.helper.make_opsetid(\"\", 13)]) # Explicitly set opset to 13\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"sqrt.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"sqrt.onnx\")\n", + "\n", + "def do_sqrt(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: sqrt(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],\n", + " [3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_sqrt(x)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_sqrt(x_np)\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [1, 2, 4]\n", + "x_example_1 = np.array([[1.0, 2.0, 4.0]], dtype=np.float32)\n", + "do_sqrt(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[ 0.25 2.25 ]\n", + "# [ 0 0.1 ]\n", + "# [ 10 1000 ]]\n", + "x_example_2 = np.array([\n", + " [0.25, 2.25],\n", + " [0.0, 0.1],\n", + " [10.0, 1000.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_sqrt(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# X =\n", + "# [[ inf nan -inf -0.0]]\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\"), -0.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_sqrt(x_example_3)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Bureau", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/sub/README.md b/safety-related-profile/sonnx/ops/spec/tests/sub/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/tanh/README.md b/safety-related-profile/sonnx/ops/spec/tests/tanh/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh.onnx b/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh.onnx new file mode 100644 index 00000000..21e56d0c Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh_doc.ipynb new file mode 100644 index 00000000..d6ce3913 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/tanh/tanh_doc.ipynb @@ -0,0 +1,757 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000]]\n", + "Result: tanh(X)=[[0.76159418,0.96402758], [0.99505472,0.99932921]]\n", + "X=[[1.00000000], [1.00000000]]\n", + "Result: tanh(X)=[[0.76159418], [0.76159418]]\n", + "X=[[0.00000000], [0.00000000]]\n", + "Result: tanh(X)=[[0.00000000], [0.00000000]]\n", + "X=[[-1.00000000], [-1.00000000]]\n", + "Result: tanh(X)=[[-0.76159418], [-0.76159418]]\n", + "X=[[inf], [inf]]\n", + "Result: tanh(X)=[[1.00000000], [1.00000000]]\n", + "X=[[-inf], [-inf]]\n", + "Result: tanh(X)=[[-1.00000000], [-1.00000000]]\n", + "X=[[nan], [nan]]\n", + "Result: tanh(X)=[[nan], [nan]]\n", + "\n", + "## Example 1\n", + "X=[[ 0.00000000, 1.00000000,-1.00000000]]\n", + "Result: tanh(X)=[[ 0.00000000, 0.76159418,-0.76159418]]\n", + "\n", + "## Example 2\n", + "X=[[-2.00000000, 0.00000000], [ 1.00000000, 2.00000000], [-4.00000000, 4.00000000]]\n", + "Result: tanh(X)=[[-0.96402758, 0.00000000], [ 0.76159418, 0.96402758], [-0.99932921, 0.99932921]]\n", + "\n", + "## Example 3\n", + "X=[[ inf, nan,-inf]]\n", + "Result: tanh(X)=[[ 1.00000000, nan,-1.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "tanh_output_name = \"TanhOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensor\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensor\n", + "input_tensor = onnx.helper.make_tensor_value_info(input_name,onnx.TensorProto.FLOAT,[None, None])\n", + "\n", + "# Create output tensor (final result after tanh operation)\n", + "tanh_output = onnx.helper.make_tensor_value_info(tanh_output_name,onnx.TensorProto.FLOAT,[None, None])\n", + "\n", + "# Define Tanh node: Y = Tanh(X)\n", + "tanh_node = onnx.helper.make_node(\"Tanh\",[input_name],[tanh_output_name])\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[tanh_node],\n", + " name=\"Tanh\",\n", + " inputs=[input_tensor],\n", + " outputs=[tanh_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Tanh available since early opsets, 13 is safe)\n", + "model = onnx.helper.make_model(graph,opset_imports=[onnx.helper.make_opsetid(\"\", 13)])\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"tanh.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"tanh.onnx\")\n", + "\n", + "def do_tanh(x):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x})\n", + "\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " # Display results\n", + " print(f\"X={x_f}\")\n", + " print(f\"Result: tanh(X)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Case N1: 2-rank tensor (float32)\n", + "x = np.array([[1.0, 2.0],\n", + " [3.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_tanh(x)\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Non nominal cases (nan, -inf, 0, negative values)\n", + "#--------------------------------------------------------------------\n", + "\n", + "v = [1.0, 0.0, -1.0, float(\"inf\"), float(\"-inf\"), float(\"nan\")]\n", + "\n", + "for x_val in v:\n", + " x_np = np.array([[x_val],\n", + " [x_val]], dtype=np.float32)\n", + " do_tanh(x_np)\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Examples from documentation\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "# Example 1\n", + "# X = [0, 1, -1]\n", + "x_example_1 = np.array([[0.0, 1.0, -1.0]], dtype=np.float32)\n", + "do_tanh(x_example_1)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "# Example 2\n", + "# X =\n", + "# [[ -2 0 ]\n", + "# [ 1 2 ]\n", + "# [ -4 4 ]]\n", + "x_example_2 = np.array([\n", + " [-2.0, 0.0],\n", + " [1.0, 2.0],\n", + " [-4.0, 4.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_tanh(x_example_2)\n", + "\n", + "print(\"\\n## Example 3\")\n", + "\n", + "# Example 3\n", + "# X =\n", + "# [[ inf nan -inf]]\n", + "x_example_3 = np.array([\n", + " [float(\"inf\"), float(\"nan\"), float(\"-inf\")]\n", + "], dtype=np.float32)\n", + "\n", + "do_tanh(x_example_3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "===================================================\n", + "Input X (float64) = [ 0.00000000e+00 1.00000000e-08 -1.00000000e-08 1.00000000e+00\n", + " -1.00000000e+00 5.00000000e+00 -5.00000000e+00 1.00000000e+01\n", + " -1.00000000e+01 2.00000000e+01 -2.00000000e+01 5.00000000e+01\n", + " -5.00000000e+01 8.00000000e+01 -8.00000000e+01 8.80000000e+01\n", + " -8.80000000e+01 1.00000000e+02 -1.00000000e+02 1.00000000e+03\n", + " -1.00000000e+03 inf -inf nan]\n", + "===================================================\n", + "\n", + "ONNX Tanh (ORT float32)\n", + "Y(float32) = [ 0.00000000e+00 9.99999905e-09 -9.99999905e-09 7.61594176e-01\n", + " -7.61594176e-01 9.99909163e-01 -9.99909163e-01 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 nan]\n", + "abs error vs fp64 = [0.00000000e+00 9.48953130e-16 9.48953130e-16 2.03366546e-08\n", + " 2.03366546e-08 4.17412328e-08 4.17412328e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "rel error vs fp64 = [0.00000000e+00 9.48953109e-08 9.48953109e-08 2.67027451e-08\n", + " 2.67027451e-08 4.17450231e-08 4.17450231e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "\n", + "Technique 1 (branching: exp(2x) if x<0 else exp(-2x))\n", + "Y(float32) = [ 0.00000000 0.00000000 0.00000000 0.76159412 -0.76159412 0.99990916\n", + " -0.99990916 1.00000000 -1.00000000 1.00000000 -1.00000000 1.00000000\n", + " -1.00000000 1.00000000 -1.00000000 1.00000000 -1.00000000 1.00000000\n", + " -1.00000000 1.00000000 -1.00000000 1.00000000 -1.00000000 nan]\n", + "abs error vs fp64 = [0.00000000e+00 1.00000000e-08 1.00000000e-08 3.92679902e-08\n", + " 3.92679902e-08 4.17412328e-08 4.17412328e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "rel error vs fp64 = [0.00000000e+00 9.99999978e-01 9.99999978e-01 5.15602567e-08\n", + " 5.15602567e-08 4.17450231e-08 4.17450231e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "\n", + "Technique 2 (single formula exp(2x) / (exp(2x)+1))\n", + "Y(float32) = [ 0.00000000 0.00000000 0.00000000 0.76159418 -0.76159412 0.99990922\n", + " -0.99990916 1.00000000 -1.00000000 1.00000000 -1.00000000 nan\n", + " -1.00000000 nan -1.00000000 nan -1.00000000 nan\n", + " -1.00000000 nan -1.00000000 nan -1.00000000 nan]\n", + "abs error vs fp64 = [0.00000000e+00 1.00000000e-08 1.00000000e-08 2.03366546e-08\n", + " 3.92679902e-08 1.78634120e-08 4.17412328e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan]\n", + "rel error vs fp64 = [0.00000000e+00 9.99999978e-01 9.99999978e-01 2.67027451e-08\n", + " 5.15602567e-08 1.78650340e-08 4.17450231e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan\n", + " 0.00000000e+00 nan 0.00000000e+00 nan]\n", + "\n", + "Technique 3 (single formula exp(-2x) / (1+exp(-2x)))\n", + "Y(float32) = [ 0.00000000 0.00000000 0.00000000 0.76159412 -0.76159418 0.99990916\n", + " -0.99990922 1.00000000 -1.00000000 1.00000000 -1.00000000 1.00000000\n", + " nan 1.00000000 nan 1.00000000 nan 1.00000000\n", + " nan 1.00000000 nan 1.00000000 nan nan]\n", + "abs error vs fp64 = [0.00000000e+00 1.00000000e-08 1.00000000e-08 3.92679902e-08\n", + " 2.03366546e-08 4.17412328e-08 1.78634120e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " nan 0.00000000e+00 nan 0.00000000e+00\n", + " nan 0.00000000e+00 nan 0.00000000e+00\n", + " nan 0.00000000e+00 nan nan]\n", + "rel error vs fp64 = [0.00000000e+00 9.99999978e-01 9.99999978e-01 5.15602567e-08\n", + " 2.67027451e-08 4.17450231e-08 1.78650340e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " nan 0.00000000e+00 nan 0.00000000e+00\n", + " nan 0.00000000e+00 nan 0.00000000e+00\n", + " nan 0.00000000e+00 nan nan]\n", + "\n", + "Technique 4 (classic: (exp(x)-exp(-x))/(exp(x)+exp(-x)))\n", + "Y(float32) = [ 0.00000000 0.00000000 0.00000000 0.76159418 -0.76159418 0.99990910\n", + " -0.99990910 1.00000000 -1.00000000 1.00000000 -1.00000000 1.00000000\n", + " -1.00000000 1.00000000 -1.00000000 1.00000000 -1.00000000 nan\n", + " nan nan nan nan nan nan]\n", + "abs error vs fp64 = [0.00000000e+00 1.00000000e-08 1.00000000e-08 2.03366546e-08\n", + " 2.03366546e-08 1.01345878e-07 1.01345878e-07 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 nan nan nan\n", + " nan nan nan nan]\n", + "rel error vs fp64 = [0.00000000e+00 9.99999978e-01 9.99999978e-01 2.67027451e-08\n", + " 2.67027451e-08 1.01355080e-07 1.01355080e-07 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 nan nan nan\n", + " nan nan nan nan]\n", + "\n", + "Technique 5 (libm-like piecewise polynomial + exp clamp)\n", + "Y(float32) = [ 0.00000000e+00 9.99999994e-09 -9.99999994e-09 7.61594117e-01\n", + " -7.61594117e-01 9.99909222e-01 -9.99909222e-01 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 nan]\n", + "abs error vs fp64 = [0.00000000e+00 6.07747099e-17 6.07747099e-17 3.92679902e-08\n", + " 3.92679902e-08 1.78634120e-08 1.78634120e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "rel error vs fp64 = [0.00000000e+00 6.07747086e-09 6.07747086e-09 5.15602567e-08\n", + " 5.15602567e-08 1.78650340e-08 1.78650340e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "\n", + "Technique 6 (ONNX-like:Eigen/MLAS polynomial rational approximation)\n", + "Y(float32) = [ 0.00000000e+00 9.99999994e-09 -9.99999994e-09 7.61594176e-01\n", + " -7.61594176e-01 9.99909163e-01 -9.99909163e-01 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 1.00000000e+00\n", + " -1.00000000e+00 1.00000000e+00 -1.00000000e+00 nan]\n", + "abs error vs fp64 = [0.00000000e+00 6.07747099e-17 6.07747099e-17 2.03366546e-08\n", + " 2.03366546e-08 4.17412328e-08 4.17412328e-08 4.12230727e-09\n", + " 4.12230727e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n", + "rel error vs fp64 = [0.00000000e+00 6.07747086e-09 6.07747086e-09 2.67027451e-08\n", + " 2.67027451e-08 4.17450231e-08 4.17450231e-08 4.12230729e-09\n", + " 4.12230729e-09 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", + " 0.00000000e+00 0.00000000e+00 0.00000000e+00 nan]\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_5140/348396083.py:21: RuntimeWarning: overflow encountered in exp\n", + " return ((np.exp(2.0 * x, dtype=np.float32) - 1.0) /\n", + "/tmp/ipykernel_5140/348396083.py:22: RuntimeWarning: overflow encountered in exp\n", + " (np.exp(2.0 * x, dtype=np.float32) + 1.0)).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:21: RuntimeWarning: invalid value encountered in divide\n", + " return ((np.exp(2.0 * x, dtype=np.float32) - 1.0) /\n", + "/tmp/ipykernel_5140/348396083.py:27: RuntimeWarning: overflow encountered in exp\n", + " return ((1.0 - np.exp(-2.0 * x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:28: RuntimeWarning: overflow encountered in exp\n", + " (1.0 + np.exp(-2.0 * x, dtype=np.float32))).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:27: RuntimeWarning: invalid value encountered in divide\n", + " return ((1.0 - np.exp(-2.0 * x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:33: RuntimeWarning: overflow encountered in exp\n", + " return ((np.exp(x, dtype=np.float32) - np.exp(-x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:34: RuntimeWarning: overflow encountered in exp\n", + " (np.exp(x, dtype=np.float32) + np.exp(-x, dtype=np.float32))).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:33: RuntimeWarning: invalid value encountered in divide\n", + " return ((np.exp(x, dtype=np.float32) - np.exp(-x, dtype=np.float32)) /\n" + ] + } + ], + "source": [ + "#--------------------------------------------------------------------\n", + "# Precision comparison: float32 implementations vs float64 reference\n", + "#--------------------------------------------------------------------\n", + "\n", + "def tanh_np_technique_1(x):\n", + " x = x.astype(np.float32)\n", + " y = np.empty_like(x, dtype=np.float32)\n", + "\n", + " mask = x < 0\n", + " y[mask] = (np.exp(2.0 * x[mask], dtype=np.float32) - 1.0) / \\\n", + " (np.exp(2.0 * x[mask], dtype=np.float32) + 1.0)\n", + "\n", + " y[~mask] = (1.0 - np.exp(-2.0 * x[~mask], dtype=np.float32)) / \\\n", + " (1.0 + np.exp(-2.0 * x[~mask], dtype=np.float32))\n", + "\n", + " return y.astype(np.float32)\n", + "\n", + "\n", + "def tanh_np_technique_2(x):\n", + " x = x.astype(np.float32)\n", + " return ((np.exp(2.0 * x, dtype=np.float32) - 1.0) /\n", + " (np.exp(2.0 * x, dtype=np.float32) + 1.0)).astype(np.float32)\n", + "\n", + "\n", + "def tanh_np_technique_3(x):\n", + " x = x.astype(np.float32)\n", + " return ((1.0 - np.exp(-2.0 * x, dtype=np.float32)) /\n", + " (1.0 + np.exp(-2.0 * x, dtype=np.float32))).astype(np.float32)\n", + "\n", + "\n", + "def tanh_np_technique_4(x):\n", + " x = x.astype(np.float32)\n", + " return ((np.exp(x, dtype=np.float32) - np.exp(-x, dtype=np.float32)) /\n", + " (np.exp(x, dtype=np.float32) + np.exp(-x, dtype=np.float32))).astype(np.float32)\n", + "\n", + "def tanh_np_technique_5(x):\n", + " x = x.astype(np.float32)\n", + " y = np.empty_like(x, dtype=np.float32)\n", + " ax = np.abs(x)\n", + "\n", + " # Region 1: very small x\n", + " m1 = ax < 2**-12\n", + " y[m1] = x[m1]\n", + "\n", + " # Region 2: |x| < 0.625 (minimax polynomial)\n", + " m2 = (ax >= 2**-12) & (ax < 0.625)\n", + " x2 = x[m2]\n", + " z = x2 * x2\n", + "\n", + " # Coefficients inspired by libm (float32)\n", + " y[m2] = x2 + x2 * z * (\n", + " np.float32(-0.333333313) +\n", + " z * (np.float32(0.133333340) +\n", + " z * np.float32(-0.053968253))\n", + " )\n", + "\n", + " # Region 3: 0.625 ≤ |x| < 9\n", + " m3 = (ax >= 0.625) & (ax < 9.0)\n", + " t = np.exp(np.float32(2.0) * ax[m3], dtype=np.float32)\n", + " y[m3] = np.sign(x[m3]) * (np.float32(1.0) - np.float32(2.0) / (t + np.float32(1.0)))\n", + "\n", + " # Region 4: saturation\n", + " m4 = ax >= 9.0\n", + " y[m4] = np.sign(x[m4])\n", + "\n", + " return y.astype(np.float32)\n", + "\n", + "def tanh_np_technique_6(x):\n", + " x = np.asarray(x, dtype=np.float32)\n", + "\n", + " # Clamp thresholds used by Eigen/MLAS (non-FMA path)\n", + " clamp = np.float32(7.90531110763549805)\n", + "\n", + " # tiny threshold where tanh(x) ~ x\n", + " tiny = np.float32(0.0004)\n", + "\n", + " # clamp\n", + " x_c = np.minimum(np.maximum(x, -clamp), clamp)\n", + "\n", + " # polynomial coefficients (Eigen/MLAS)\n", + " a1 = np.float32(4.89352455891786e-03)\n", + " a3 = np.float32(6.37261928875436e-04)\n", + " a5 = np.float32(1.48572235717979e-05)\n", + " a7 = np.float32(5.12229709037114e-08)\n", + " a9 = np.float32(-8.60467152213735e-11)\n", + " a11 = np.float32(2.00018790482477e-13)\n", + " a13 = np.float32(-2.76076847742355e-16)\n", + "\n", + " b0 = np.float32(4.89352518554385e-03)\n", + " b2 = np.float32(2.26843463243900e-03)\n", + " b4 = np.float32(1.18534705686654e-04)\n", + " b6 = np.float32(1.19825839466702e-06)\n", + "\n", + " x2 = x_c * x_c\n", + "\n", + " # numerator polynomial p(x)\n", + " p = a13\n", + " p = p * x2 + a11\n", + " p = p * x2 + a9\n", + " p = p * x2 + a7\n", + " p = p * x2 + a5\n", + " p = p * x2 + a3\n", + " p = p * x2 + a1\n", + " p = p * x_c\n", + "\n", + " # denominator polynomial q(x)\n", + " q = b6\n", + " q = q * x2 + b4\n", + " q = q * x2 + b2\n", + " q = q * x2 + b0\n", + "\n", + " y = p / q\n", + "\n", + " # tiny x => tanh(x) ~ x\n", + " y = np.where(np.abs(x) < tiny, x, y)\n", + "\n", + " return y.astype(np.float32)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "def compare_precision_vs_fp64(x_fp64):\n", + " print(\"\\n===================================================\")\n", + " print(f\"Input X (float64) = {x_fp64.flatten()}\")\n", + " print(\"===================================================\")\n", + "\n", + " # Reference: float64 numpy tanh\n", + " y_ref = np.tanh(x_fp64) # float64 reference\n", + "\n", + " # Cast input to float32 for tested implementations\n", + " x_f32 = x_fp64.astype(np.float32)\n", + "\n", + " results = {\n", + " \"ONNX Tanh (ORT float32)\": session.run(None, {input_name: x_f32})[0].astype(np.float32),\n", + " \"Technique 1 (branching: exp(2x) if x<0 else exp(-2x))\": tanh_np_technique_1(x_f32),\n", + " \"Technique 2 (single formula exp(2x) / (exp(2x)+1))\": tanh_np_technique_2(x_f32),\n", + " \"Technique 3 (single formula exp(-2x) / (1+exp(-2x)))\": tanh_np_technique_3(x_f32),\n", + " \"Technique 4 (classic: (exp(x)-exp(-x))/(exp(x)+exp(-x)))\": tanh_np_technique_4(x_f32),\n", + " \"Technique 5 (libm-like piecewise polynomial + exp clamp)\": tanh_np_technique_5(x_f32),\n", + " \"Technique 6 (ONNX-like:Eigen/MLAS polynomial rational approximation)\": tanh_np_technique_6(x_f32),\n", + " }\n", + "\n", + " for name, y_f32 in results.items():\n", + " y_f64 = y_f32.astype(np.float64)\n", + "\n", + " abs_err = np.abs(y_f64 - y_ref)\n", + " rel_err = abs_err / (np.abs(y_ref) + np.finfo(np.float64).eps)\n", + "\n", + " print(f\"\\n{name}\")\n", + " print(f\"Y(float32) = {y_f32.flatten()}\")\n", + " print(f\"abs error vs fp64 = {abs_err.flatten()}\")\n", + " print(f\"rel error vs fp64 = {rel_err.flatten()}\")\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Stress test values (chosen to expose numerical limits)\n", + "#--------------------------------------------------------------------\n", + "\n", + "test_values_fp64 = np.array([\n", + " 0.0,\n", + " 1e-8,\n", + " -1e-8,\n", + " 1.0,\n", + " -1.0,\n", + " 5.0,\n", + " -5.0,\n", + " 10.0,\n", + " -10.0,\n", + " 20.0,\n", + " -20.0,\n", + " 50.0,\n", + " -50.0,\n", + " 80.0,\n", + " -80.0,\n", + " 88.0, # near float32 exp overflow\n", + " -88.0,\n", + " 100.0,\n", + " -100.0,\n", + " 1e3,\n", + " -1e3,\n", + " np.inf,\n", + " -np.inf,\n", + " np.nan\n", + "], dtype=np.float64)\n", + "\n", + "# 2-rank tensor as required by the ONNX model\n", + "x_test_fp64 = test_values_fp64.reshape(-1, 1)\n", + "\n", + "compare_precision_vs_fp64(x_test_fp64)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "==================== RANKING ====================\n", + "Best -> Worst (based on max abs error)\n", + "------------------------------------------------\n", + "Rank | Technique | mean_abs | max_abs | rms\n", + " 1 | Technique 5 (libm-like piecewise polynomial + exp clamp) | 5.326e-09 | 3.927e-08 | 1.278e-08\n", + " 2 | Technique 6 (ONNX-like:Eigen/MLAS polynomial rational approximation) | 5.757e-09 | 4.174e-08 | 1.375e-08\n", + " 3 | ONNX Tanh (ORT float32) | 5.757e-09 | 4.174e-08 | 1.375e-08\n", + " 4 | Technique 1 (branching: exp(2x) if x<0 else exp(-2x)) | 8.272e-09 | 4.174e-08 | 1.720e-08\n", + " 5 | Technique 2 (single formula exp(2x) / (exp(2x)+1)) | 8.674e-09 | 4.174e-08 | 1.581e-08\n", + " 6 | Technique 3 (single formula exp(-2x) / (1+exp(-2x))) | 8.674e-09 | 4.174e-08 | 1.581e-08\n", + " 7 | Technique 4 (classic: (exp(x)-exp(-x))/(exp(x)+exp(-x))) | 1.598e-08 | 1.013e-07 | 3.565e-08\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/tmp/ipykernel_5140/348396083.py:21: RuntimeWarning: overflow encountered in exp\n", + " return ((np.exp(2.0 * x, dtype=np.float32) - 1.0) /\n", + "/tmp/ipykernel_5140/348396083.py:22: RuntimeWarning: overflow encountered in exp\n", + " (np.exp(2.0 * x, dtype=np.float32) + 1.0)).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:21: RuntimeWarning: invalid value encountered in divide\n", + " return ((np.exp(2.0 * x, dtype=np.float32) - 1.0) /\n", + "/tmp/ipykernel_5140/348396083.py:27: RuntimeWarning: overflow encountered in exp\n", + " return ((1.0 - np.exp(-2.0 * x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:28: RuntimeWarning: overflow encountered in exp\n", + " (1.0 + np.exp(-2.0 * x, dtype=np.float32))).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:27: RuntimeWarning: invalid value encountered in divide\n", + " return ((1.0 - np.exp(-2.0 * x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:33: RuntimeWarning: overflow encountered in exp\n", + " return ((np.exp(x, dtype=np.float32) - np.exp(-x, dtype=np.float32)) /\n", + "/tmp/ipykernel_5140/348396083.py:34: RuntimeWarning: overflow encountered in exp\n", + " (np.exp(x, dtype=np.float32) + np.exp(-x, dtype=np.float32))).astype(np.float32)\n", + "/tmp/ipykernel_5140/348396083.py:33: RuntimeWarning: invalid value encountered in divide\n", + " return ((np.exp(x, dtype=np.float32) - np.exp(-x, dtype=np.float32)) /\n" + ] + }, + { + "data": { + "text/plain": [ + "[('Technique 5 (libm-like piecewise polynomial + exp clamp)',\n", + " np.float64(5.326409521631861e-09),\n", + " np.float64(3.926799019282612e-08),\n", + " np.float64(1.2779296974320608e-08)),\n", + " ('Technique 6 (ONNX-like:Eigen/MLAS polynomial rational approximation)',\n", + " np.float64(5.756538671320927e-09),\n", + " np.float64(4.174123280353825e-08),\n", + " np.float64(1.3745840328355247e-08)),\n", + " ('ONNX Tanh (ORT float32)',\n", + " np.float64(5.756538748553834e-09),\n", + " np.float64(4.174123280353825e-08),\n", + " np.float64(1.374584032835525e-08)),\n", + " ('Technique 1 (branching: exp(2x) if x<0 else exp(-2x))',\n", + " np.float64(8.272306979971962e-09),\n", + " np.float64(4.174123280353825e-08),\n", + " np.float64(1.719782338856382e-08)),\n", + " ('Technique 2 (single formula exp(2x) / (exp(2x)+1))',\n", + " np.float64(8.67375906455339e-09),\n", + " np.float64(4.174123280353825e-08),\n", + " np.float64(1.5813198900882747e-08)),\n", + " ('Technique 3 (single formula exp(-2x) / (1+exp(-2x)))',\n", + " np.float64(8.67375906455339e-09),\n", + " np.float64(4.174123280353825e-08),\n", + " np.float64(1.5813198900882747e-08)),\n", + " ('Technique 4 (classic: (exp(x)-exp(-x))/(exp(x)+exp(-x)))',\n", + " np.float64(1.5977039933506657e-08),\n", + " np.float64(1.0134587757892888e-07),\n", + " np.float64(3.564788557232498e-08))]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def rank_techniques(x_fp64, session, input_name):\n", + " # Reference\n", + " y_ref = np.tanh(x_fp64)\n", + "\n", + " # input float32\n", + " x_f32 = x_fp64.astype(np.float32)\n", + "\n", + " results = {\n", + " \"ONNX Tanh (ORT float32)\": session.run(None, {input_name: x_f32})[0].astype(np.float32),\n", + " \"Technique 1 (branching: exp(2x) if x<0 else exp(-2x))\": tanh_np_technique_1(x_f32),\n", + " \"Technique 2 (single formula exp(2x) / (exp(2x)+1))\": tanh_np_technique_2(x_f32),\n", + " \"Technique 3 (single formula exp(-2x) / (1+exp(-2x)))\": tanh_np_technique_3(x_f32),\n", + " \"Technique 4 (classic: (exp(x)-exp(-x))/(exp(x)+exp(-x)))\": tanh_np_technique_4(x_f32),\n", + " \"Technique 5 (libm-like piecewise polynomial + exp clamp)\": tanh_np_technique_5(x_f32),\n", + " \"Technique 6 (ONNX-like:Eigen/MLAS polynomial rational approximation)\": tanh_np_technique_6(x_f32),\n", + " }\n", + "\n", + "\n", + " ranking = []\n", + "\n", + " for name, y_f32 in results.items():\n", + " y_f64 = y_f32.astype(np.float64)\n", + "\n", + " abs_err = np.abs(y_f64 - y_ref)\n", + "\n", + " # ignore NaNs for metrics\n", + " abs_err_clean = abs_err[np.isfinite(abs_err)]\n", + "\n", + " mean_abs = np.mean(abs_err_clean)\n", + " max_abs = np.max(abs_err_clean)\n", + " rms_abs = np.sqrt(np.mean(abs_err_clean**2))\n", + "\n", + " ranking.append((name, mean_abs, max_abs, rms_abs))\n", + "\n", + " # Sort by max error first, then mean, then rms\n", + " ranking_sorted = sorted(ranking, key=lambda t: (t[2], t[1], t[3]))\n", + "\n", + " # Display\n", + " print(\"\\n==================== RANKING ====================\")\n", + " print(\"Best -> Worst (based on max abs error)\")\n", + " print(\"------------------------------------------------\")\n", + " print(\"Rank | Technique | mean_abs | max_abs | rms\")\n", + " for i, (name, mean_abs, max_abs, rms_abs) in enumerate(ranking_sorted, start=1):\n", + " print(f\"{i:>4d} | {name:<20s} | {mean_abs:.3e} | {max_abs:.3e} | {rms_abs:.3e}\")\n", + "\n", + " return ranking_sorted\n", + "\n", + "\n", + "# ------------------------------------------------------------\n", + "# Stress test values\n", + "# ------------------------------------------------------------\n", + "test_values_fp64 = np.array([\n", + " 0.0,\n", + " 1e-8,\n", + " -1e-8,\n", + " 1.0,\n", + " -1.0,\n", + " 5.0,\n", + " -5.0,\n", + " 10.0,\n", + " -10.0,\n", + " 20.0,\n", + " -20.0,\n", + " 50.0,\n", + " -50.0,\n", + " 80.0,\n", + " -80.0,\n", + " 88.0,\n", + " -88.0,\n", + " 100.0,\n", + " -100.0,\n", + " 1e3,\n", + " -1e3,\n", + " np.inf,\n", + " -np.inf,\n", + " np.nan\n", + "], dtype=np.float64)\n", + "\n", + "x_test_fp64 = test_values_fp64.reshape(-1, 1)\n", + "\n", + "# ------------------------------------------------------------\n", + "# Call ranking (replace session/input_name by ton ONNX session)\n", + "# ------------------------------------------------------------\n", + "rank_techniques(x_test_fp64, session, input_name)\n" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python (.venv)", + "language": "python", + "name": "venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/READEME.md b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/READEME.md new file mode 100644 index 00000000..5c5f7700 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/READEME.md @@ -0,0 +1,33 @@ +# How to run +There are two main scripts, test_unsqueeze.py and check_edges.py + +Before run any of those scripts you need to have the requirements that are presented at requirements.txt + +To run the test_unsqueeze.py you need to do +```bash +pytest test_unsqueeze.py +``` +Or if you want to see more information about the tests +```bash +pytest -s test_unsqueeze.py +``` +This script will generate a json file with all the tests that were generated. + +To run check_edges.py you need to do +```bash +python check_edges.py +``` +Make sure that you have the json file generated by test_unsqueeze.py + +# Explanations of Unsqueeze corner and edge cases generation + +## Assumptions +First of all let us explain what we mean by corner and edge cases. +- **Corner cases** are the extreme values of a given parameter, such as the minimum and maximum values that the parameter can take. + +- **Edge cases** are values that are just above or below the minimum and maximum values, respectively. + +## Unsqueeze tests +For the unsqueeze operator we just **checked both the corner and edge cases** for the **shape_size_input_x**, **size_input_x**, **size_input_axes** and **x_type** parameters. + +We consider **shape_size_input_x**, **size_input_x**, **size_input_axes** and **x_type** as **lines** since they are independent and we checked the corner and edge cases for all of them. \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/check_edges.py b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/check_edges.py new file mode 100644 index 00000000..acf21f26 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/check_edges.py @@ -0,0 +1,122 @@ +import json +import numpy as np +import ml_dtypes + + +unsqueeze_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BFLOAT16": ml_dtypes.bfloat16, + "BOOL": np.bool_, + "STRING": np.str_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +def check_edges(): + """ + Check edge cases in the generated data + """ + with open("generated_data.json", "r") as f: + data = json.load(f) + check_shape_size_input_x = check_individual_variables( + "shape_size_input_x", + data["shape_size_input_x"], + data["min_shape_size_input_x"], + data["max_shape_size_input_x"] + ) + size_input_x_axis = [ item for sublist in data["size_input_x"] for item in sublist ] + check_size_input_x = check_individual_variables( + "size_input_x", + size_input_x_axis, + data["min_size_input_x"], + data["max_size_input_x"] + ) + check_size_input_axes = check_individual_variables( + "size_input_axes", + data["size_input_axes"], + data["min_number_of_axes"], + data["max_number_of_axes"] + ) + provider = data["ONNXRuntime_Provider"] + check_x_type = check_type("x_type", data["x_type"], provider) + + return all([check_shape_size_input_x, check_size_input_x, check_size_input_axes]) + + +""" +Check individual variable analysis +""" +def check_individual_variables(variable_name, variable_analysis, min, max): + corner_cases = [min, max] + corner_cases_test = all(corner_case in variable_analysis for corner_case in corner_cases) + + has_mean_case_mean = any(d for d in variable_analysis if d > min and d < max) + has_out_of_bounds = not(any(d for d in variable_analysis if d < min or d > max)) + + print(f"Individual variable analysis: {variable_name}") + if not corner_cases_test: + for corner_case in corner_cases: + if corner_case not in variable_analysis: + print(f" - Missing corner case: {corner_case}") + if not has_mean_case_mean: + print(f" - Missing mean case") + if not has_out_of_bounds: + print(f" - Found out of bounds values") + + return all([corner_cases_test, has_mean_case_mean, has_out_of_bounds]) + +def check_type(variable_name, variable_analysis, provider): + """ + Check input types + """ + supported_types = set(unsqueeze_types.get(provider).keys()) + variable_analysis_types = set(variable_analysis) + missing_types = supported_types - variable_analysis_types + print(f"Type analysis: {variable_name}") + if missing_types: + for missing_type in missing_types: + print(f" - Missing type: {missing_type}") + return False + return True + +check_edges() \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/requirements.txt b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/requirements.txt new file mode 100644 index 00000000..1987c54d --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/requirements.txt @@ -0,0 +1,6 @@ +pytest +numpy +hypothesis +onnx +onnxruntime +tensorflow \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/test_unsqueeze.py b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/test_unsqueeze.py new file mode 100644 index 00000000..3edc0eb7 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/hypothesis/test_unsqueeze.py @@ -0,0 +1,320 @@ +""" +This file uses the Hypothesis library to generate a wide range of test cases +for the Unsqueeze operation in ONNX. +""" +import os + +import json +import numpy as np +import ml_dtypes + +from onnx import helper +import onnx.checker +from onnxruntime import InferenceSession +import onnx.reference + +import tensorflow as tf + +from hypothesis import given, settings +import hypothesis.extra.numpy as hnp +from hypothesis import strategies as st + +if os.path.exists("generated_data.json"): + os.remove("generated_data.json") + +""" +Inputs/attributes details +""" +inputs_attributes = { + "min_shape_size_input_x": 0, # Adjust as needed + "max_shape_size_input_x": 4, # Adjust as needed + "min_size_input_x_axis": 0, # Adjust as needed ( Minimum 0 ) + "max_size_input_x_axis": 10, # Adjust as needed + "min_number_of_axes": 0, # Adjust as needed ( Minimum 0 ) + "max_number_of_axes": 4, # Adjust as needed + "ONNXRuntime_Provider": "CPUExecutionProvider" # available providers are CPUExecutionProvider, CUDAExecutionProvider, DmlExecutionProvider +} + +""" +Unsqueeze supported types +""" +unsqueeze_types = { + "CPUExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BFLOAT16": ml_dtypes.bfloat16, + "BOOL": np.bool_, + "STRING": np.str_ + }, + "CUDAExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_, + "BFLOAT16": ml_dtypes.bfloat16 + }, + "DmlExecutionProvider": { + "INT8": np.int8, + "INT16": np.int16, + "INT32": np.int32, + "INT64": np.int64, + "UINT8": np.uint8, + "UINT16": np.uint16, + "UINT32": np.uint32, + "UINT64": np.uint64, + "FP16": np.float16, + "FP32": np.float32, + "FP64": np.float64, + "BOOL": np.bool_ + } +} + + +""" +Store generated data +""" +generated_data = { + "shape_size_input_x" : [], + "size_input_x": [], + "size_input_axes": [], + "x_type": [] +} + +dtype_to_key = {v: k for k, v in unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"]).items()} + +""" +Function to generate valid unsqueeze arguments +""" +@st.composite +def valid_unsqueeze_args(draw): + #--------------------------------------------------- + # Restrictions + #--------------------------------------------------- + # X [C1] - Type Consistency + all_valid_types = list(unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"]).keys()) + dtype_name = draw(st.sampled_from(all_valid_types)) + dtype = unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"])[dtype_name] + + if np.issubdtype(dtype, np.integer): + min_value = np.iinfo(dtype).min + max_value = np.iinfo(dtype).max + a_numeric = st.integers(min_value=min_value, max_value=max_value) + elif np.issubdtype(dtype, np.floating): + min_value = np.finfo(dtype).min + max_value = np.finfo(dtype).max + a_numeric = st.floats(min_value=min_value, max_value=max_value) + elif np.issubdtype(dtype, np.bool_): + a_numeric = st.booleans() + elif np.issubdtype(dtype, np.str_): + a_numeric = st.text( + alphabet=st.characters(codec="utf-8", blacklist_characters='\x00') + ) + elif dtype == ml_dtypes.bfloat16: + min_value = float(ml_dtypes.finfo(unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).min) + max_value = float(ml_dtypes.finfo(unsqueeze_types.get(inputs_attributes["ONNXRuntime_Provider"])["BFLOAT16"]).max) + a_numeric = st.floats(min_value=min_value, max_value=max_value) + + #--------------------------------------------------- + # Input X + #--------------------------------------------------- + + # shape_size_input_x is the number of dimensions of input tensor X + shape_size_input_x = draw(st.integers( + min_value=inputs_attributes["min_shape_size_input_x"], + max_value=inputs_attributes["max_shape_size_input_x"])) + + # x_shape is the shape of input tensor X + x_shape = [] + for _ in range(shape_size_input_x): + dim = draw(st.integers( + min_value=inputs_attributes["min_size_input_x_axis"], + max_value=inputs_attributes["max_size_input_x_axis"])) + x_shape.append(dim) + + # Create input tensor X + if dtype_name == "BFLOAT16": + # X [C1] + temp_tensor = draw(hnp.arrays(dtype=np.float32, shape=x_shape, elements=a_numeric)) + tf_tensor = tf.cast(tf.constant(temp_tensor), tf.bfloat16) + x = tf_tensor.numpy() + else: + # X [C1] + x = draw(hnp.arrays(dtype=dtype, shape=x_shape, elements=a_numeric)) + + #--------------------------------------------------- + # Input A + #--------------------------------------------------- + + # number_of_axes is the number of axes to unsqueeze + number_of_axes = draw(st.integers( + min_value=inputs_attributes["min_number_of_axes"], + max_value=inputs_attributes["max_number_of_axes"])) + + # r is the rank of output tensor Y + r = shape_size_input_x + number_of_axes + # all_valid_axes is the list of all non negative axes to unsqueeze + # A [C1], # A [C2] + all_valid_axes = list(range(r)) + # axes is the list of axes to unsqueeze + axes_values = draw(st.permutations(all_valid_axes))[:number_of_axes] + + # Create input tensor A + a = [] + for a_val in axes_values: + # Randomly decide to use negative or positive axis representation + use_negative = draw(st.booleans()) + if use_negative: + a.append(a_val - r) + else: + a.append(a_val) + a = np.array(a, dtype=np.int64) + a_normalized = np.where(a < 0, a + r, a) + + #--------------------------------------------------- + # Output Y + #--------------------------------------------------- + + # Y [C1] + output_shape = list(x.shape) + for axis in sorted(a_normalized): + pos = axis if axis >= 0 else len(output_shape) + axis + 1 + output_shape.insert(pos, 1) + + return x, a, output_shape, dtype_name + +""" +Function that runs the test +""" +@settings(max_examples=10000, deadline=None) +@given(valid_unsqueeze_args()) +def test_slice(args): + x, a, output_shape, dtype_name = args + x_type_key = dtype_to_key.get(x.dtype.type, str(x.dtype)) + generated_data["x_type"].append(x_type_key) + generated_data["shape_size_input_x"].append(len(x.shape)) + generated_data["size_input_x"].append(x.shape) + generated_data["size_input_axes"].append(len(a)) + + y = run_onnx_unsqueeze(x, a, output_shape, dtype_name) + check_constraints(x, a, y, output_shape) + +def teardown_module(): + """ + Function to write generated data to a json file + """ + data = { + "title": "Data generated for testing ONNX Unsqueeze Operator", + "min_shape_size_input_x": inputs_attributes["min_shape_size_input_x"], + "max_shape_size_input_x": inputs_attributes["max_shape_size_input_x"], + "shape_size_input_x": generated_data["shape_size_input_x"], + "min_size_input_x": inputs_attributes["min_size_input_x_axis"], + "max_size_input_x": inputs_attributes["max_size_input_x_axis"], + "size_input_x": generated_data["size_input_x"], + "min_number_of_axes": inputs_attributes["min_number_of_axes"], + "max_number_of_axes": inputs_attributes["max_number_of_axes"], + "size_input_axes": generated_data["size_input_axes"], + "ONNXRuntime_Provider": inputs_attributes["ONNXRuntime_Provider"], + "x_type": generated_data["x_type"] + } + + with open("generated_data.json", "w", encoding="utf-8") as f: + json.dump(data, f, indent=4) + +def run_onnx_unsqueeze(x, a, output_shape, dtype_name): + """ + Function that runs the ONNX Unsqueeze operation + """ + + # Create inputs + x_onnx = helper.make_tensor_value_info('x', + helper.np_dtype_to_tensor_dtype(x.dtype), + x.shape) + a_onnx = helper.make_tensor_value_info('a', + helper.np_dtype_to_tensor_dtype(a.dtype), + a.shape) + + node_def = helper.make_node( + 'Unsqueeze', + ['x', 'a'], + ['y'] + ) + + # Create the graph + graph_def = helper.make_graph( + [node_def], + 'test-unsqueeze', + [x_onnx, a_onnx], + # Y [C2] + [helper.make_tensor_value_info('y', + helper.np_dtype_to_tensor_dtype(x.dtype), + output_shape)], + ) + + onnx_model = helper.make_model(graph_def) + + #Let's freeze the opset. + del onnx_model.opset_import[:] + opset = onnx_model.opset_import.add() + opset.domain = '' + opset.version = 22 + onnx_model.ir_version = 10 + + # Verify the model + onnx.checker.check_model(onnx_model) + + # Do inference + if dtype_name in ["BFLOAT16"]: + # Use ONNX Reference Implementation for bfloat16 + # BFLOAT16 is not supported by ONNX Runtime while using numpy + # An alternative is to use torch tensores and CUDAProvider + sess = onnx.reference.ReferenceEvaluator(onnx_model) + else: + # Use ONNX Runtime for other types + sess = InferenceSession(onnx_model.SerializeToString(), + providers=["CPUExecutionProvider"]) + + y = sess.run(None, {'x': x, 'a': a})[0] + + print("x shape:", x.shape) + print("a values:", a) + print("y shape:", y.shape) + return y + +def check_constraints(x, a, y, output_shape): + """ + Function that defines asserts for the constraints + """ + + # X[C1], Y[C2] -> X[C1] + x_is_string = np.issubdtype(x.dtype, np.str_) or np.issubdtype(x.dtype, np.object_) + y_is_string = np.issubdtype(y.dtype, np.str_) or np.issubdtype(y.dtype, np.object_) + if x_is_string and y_is_string: + pass + else: + assert x.dtype == y.dtype + # A [C1] + r = len(y.shape) + assert np.all((a >= -r) & (a < r)) + # A [C2] + a_normalized = (a + r) % r + assert len(np.unique(a_normalized)) == len(a_normalized) + # Y [C1] + assert list(y.shape) == output_shape + \ No newline at end of file diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.ipynb b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.ipynb new file mode 100644 index 00000000..8a054838 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.ipynb @@ -0,0 +1,291 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 33, + "id": "245013fc", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: onnx in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.19.0)\n", + "Requirement already satisfied: onnxruntime in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (1.22.1)\n", + "Requirement already satisfied: numpy>=1.22 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (2.2.6)\n", + "Requirement already satisfied: protobuf>=4.25.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (6.32.1)\n", + "Requirement already satisfied: typing_extensions>=4.7.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (4.15.0)\n", + "Requirement already satisfied: ml_dtypes in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnx) (0.5.3)\n", + "Requirement already satisfied: coloredlogs in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (15.0.1)\n", + "Requirement already satisfied: flatbuffers in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.2.10)\n", + "Requirement already satisfied: packaging in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (25.0)\n", + "Requirement already satisfied: sympy in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from onnxruntime) (1.14.0)\n", + "Requirement already satisfied: humanfriendly>=9.1 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from coloredlogs->onnxruntime) (10.0)\n", + "Requirement already satisfied: mpmath<1.4,>=1.1.0 in /home/rm-silva/venvs/hypothesis-env/lib/python3.13/site-packages (from sympy->onnxruntime) (1.3.0)\n" + ] + } + ], + "source": [ + "!pip install onnx onnxruntime" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "31fa9bc1", + "metadata": {}, + "outputs": [], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name = \"X\"\n", + "input_axis_name = \"A\"\n", + "unsqueeze_output_name = \"Y\"\n", + "\n", + "# Create the ONNX model with Unsqueeze operator\n", + "def create_unsqueeze_model(axis, input_rank, output_shape, dtype, axis_dtype):\n", + "\n", + " #Create input tensor\n", + " input_x = onnx.helper.make_tensor_value_info(input_name, dtype, input_rank)\n", + " input_axis = onnx.helper.make_tensor_value_info(input_axis_name, axis_dtype, [len(axis)])\n", + "\n", + " # Create output tensor (final result after unsqueeze operation)\n", + " unsqueeze_output = onnx.helper.make_tensor_value_info(unsqueeze_output_name, dtype, output_shape)\n", + "\n", + " # Define unsqueeze node\n", + " unsqueeze_node = onnx.helper.make_node(\n", + " \"Unsqueeze\",\n", + " inputs=[input_name, input_axis_name],\n", + " outputs=[unsqueeze_output_name],\n", + " )\n", + "\n", + " # Create the ONNX graph\n", + " graph_def = onnx.helper.make_graph(\n", + " [unsqueeze_node],\n", + " \"Unsqueeze\",\n", + " [input_x, input_axis],\n", + " [unsqueeze_output],\n", + " )\n", + "\n", + " # Create the ONNX model\n", + " model = onnx.helper.make_model(graph_def, opset_imports=[onnx.helper.make_opsetid(\"\", 22)])\n", + " model.ir_version = 10 \n", + " onnx.checker.check_model(model)\n", + "\n", + " # Save the model\n", + " onnx.save(model, \"unsqueeze.onnx\")\n", + "\n", + " # Load and run the model with ONNX Runtime\n", + " session = ort.InferenceSession(\"unsqueeze.onnx\")\n", + " return session\n", + "\n", + "def do_unsqueeze(x, axis, session):\n", + " # Run inference\n", + " output = session.run(None, {input_name: x, input_axis_name: axis})\n", + "\n", + " x_f = (np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + " y_f = (np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', ''))\n", + "\n", + " # Display results\n", + " print(\"Shape of input tensor:\", x.shape)\n", + " print(f\"X={x_f}\")\n", + " print(\"Shape of output tensor:\", output[0].shape)\n", + " print(f\"Result = {y_f}\")\n", + "\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "c66a3e9a", + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_output_shape(x, a):\n", + " r = len(x.shape) + len(a)\n", + " a_normalized = []\n", + " for a_value in a:\n", + " if a_value < 0:\n", + " a_normalized.append(a_value + r)\n", + " else:\n", + " a_normalized.append(a_value)\n", + "\n", + " output_shape = list(x.shape)\n", + " for axis in sorted(a_normalized):\n", + " pos = axis if axis >= 0 else len(output_shape) + axis + 1\n", + " output_shape.insert(pos, 1)\n", + " return output_shape" + ] + }, + { + "cell_type": "markdown", + "id": "b9167448", + "metadata": {}, + "source": [ + "## Nominal Cases" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fda05307", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1, 2, 3, 4)\n", + "Result = [[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]]\n" + ] + } + ], + "source": [ + "# Case N1: 3-rank tensor (int32), axes = [0]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([0]).astype(np.int64) \n", + "input_shape = list(x.shape)\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b9682e0a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (2, 3, 4, 1)\n", + "Result = [[[[ 0], [ 1], [ 2], [ 3]], [[ 4], [ 5], [ 6], [ 7]], [[ 8], [ 9], [10], [11]]], [[[12], [13], [14], [15]], [[16], [17], [18], [19]], [[20], [21], [22], [23]]]]\n" + ] + } + ], + "source": [ + "# Case N2: 3-rank tensor (int32), axes = [-1]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([-1]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "43e09eae", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Shape of input tensor: (2, 3, 4)\n", + "X=[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]\n", + "Shape of output tensor: (1, 1, 2, 3, 4)\n", + "Result = [[[[[ 0, 1, 2, 3], [ 4, 5, 6, 7], [ 8, 9,10,11]], [[12,13,14,15], [16,17,18,19], [20,21,22,23]]]]]\n" + ] + } + ], + "source": [ + "# Case N3: 3-rank tensor (int32), axes = [0,1]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([0,1]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "388ac5b2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\u001b[1;31m2025-12-10 11:57:46.491544509 [E:onnxruntime:, sequential_executor.cc:572 ExecuteKernel] Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n", + "\u001b[m\n" + ] + }, + { + "ename": "RuntimeException", + "evalue": "[ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n", + "output_type": "error", + "traceback": [ + "\u001b[31m---------------------------------------------------------------------------\u001b[39m", + "\u001b[31mRuntimeException\u001b[39m Traceback (most recent call last)", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[9]\u001b[39m\u001b[32m, line 9\u001b[39m\n\u001b[32m 7\u001b[39m output_shape = calculate_output_shape(x, axes)\n\u001b[32m 8\u001b[39m session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n\u001b[32m----> \u001b[39m\u001b[32m9\u001b[39m \u001b[43mdo_unsqueeze\u001b[49m\u001b[43m(\u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43maxes\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msession\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 49\u001b[39m, in \u001b[36mdo_unsqueeze\u001b[39m\u001b[34m(x, axis, session)\u001b[39m\n\u001b[32m 47\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mdo_unsqueeze\u001b[39m(x, axis, session):\n\u001b[32m 48\u001b[39m \u001b[38;5;66;03m# Run inference\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m49\u001b[39m output = \u001b[43msession\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[43minput_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mx\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_axis_name\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m}\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 51\u001b[39m x_f = (np.array2string(x, separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n\u001b[32m 52\u001b[39m y_f = (np.array2string(output[\u001b[32m0\u001b[39m], separator=\u001b[33m'\u001b[39m\u001b[33m,\u001b[39m\u001b[33m'\u001b[39m, max_line_width=np.inf).replace(\u001b[33m'\u001b[39m\u001b[38;5;130;01m\\n\u001b[39;00m\u001b[33m'\u001b[39m, \u001b[33m'\u001b[39m\u001b[33m'\u001b[39m))\n", + "\u001b[36mFile \u001b[39m\u001b[32m~/venvs/hypothesis-env/lib/python3.13/site-packages/onnxruntime/capi/onnxruntime_inference_collection.py:273\u001b[39m, in \u001b[36mSession.run\u001b[39m\u001b[34m(self, output_names, input_feed, run_options)\u001b[39m\n\u001b[32m 271\u001b[39m output_names = [output.name \u001b[38;5;28;01mfor\u001b[39;00m output \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m._outputs_meta]\n\u001b[32m 272\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m273\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_sess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43moutput_names\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43minput_feed\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_options\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 274\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m C.EPFail \u001b[38;5;28;01mas\u001b[39;00m err:\n\u001b[32m 275\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m._enable_fallback:\n", + "\u001b[31mRuntimeException\u001b[39m: [ONNXRuntimeError] : 6 : RUNTIME_EXCEPTION : Non-zero status code returned while running Unsqueeze node. Name:'' Status Message: /onnxruntime_src/onnxruntime/core/providers/common.h:31 int64_t onnxruntime::HandleNegativeAxis(int64_t, int64_t) IsAxisInRange(axis, tensor_rank) was false. axis 4 is not in valid range [-4,3]\n" + ] + } + ], + "source": [ + "# This cell must fail. The axis value is out of range\n", + "# Case N4: 3-rank tensor (int32), axes = [4]\n", + "onnx_type = onnx.TensorProto.INT32\n", + "axes_type = onnx.TensorProto.INT64\n", + "x = np.arange(24).reshape(2, 3, 4).astype(np.int32)\n", + "axes = np.array([4]).astype(np.int64) \n", + "input_shape = x.shape\n", + "output_shape = calculate_output_shape(x, axes)\n", + "session = create_unsqueeze_model(axes, input_shape, output_shape, onnx_type, axes_type)\n", + "do_unsqueeze(x, axes, session)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "96a1ee35", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "hypothesis-env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.onnx b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.onnx new file mode 100644 index 00000000..ad6b075b Binary files /dev/null and b/safety-related-profile/sonnx/ops/spec/tests/unsqueeze/unsqueeze.onnx differ diff --git a/safety-related-profile/sonnx/ops/spec/tests/where/README.md b/safety-related-profile/sonnx/ops/spec/tests/where/README.md new file mode 100644 index 00000000..e69de29b diff --git a/safety-related-profile/sonnx/ops/spec/tests/where/where_doc.ipynb b/safety-related-profile/sonnx/ops/spec/tests/where/where_doc.ipynb new file mode 100644 index 00000000..36756e27 --- /dev/null +++ b/safety-related-profile/sonnx/ops/spec/tests/where/where_doc.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "colab_type": "text", + "id": "view-in-github" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "rEKyVwTun7hf", + "outputId": "56e0c81b-6aeb-4e95-f638-b7afb0dcd870" + }, + "outputs": [], + "source": [ + "\n", + "#!pip install onnx onnxruntime\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cYvhVMxXoRwo", + "outputId": "e9e35640-cabf-4728-f877-f152435eab0e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "## Example 1\n", + "condition=[[ True,False, True]]\n", + "X=[[9.00000000,8.00000000,7.09999990]]\n", + "Y=[[6.00000000,5.00000000,4.00000000]]\n", + "Result: Where(condition,X,Y)=[[9.00000000,5.00000000,7.09999990]]\n", + "\n", + "## Example 2\n", + "condition=[[ True, True], [ True,False], [False, True]]\n", + "X=[[1.00000000,2.00000000], [3.00000000,4.00000000], [5.00000000,6.00000000]]\n", + "Y=[[12.00000000,11.00000000], [10.00000000, 9.00000000], [ 8.00000000, 7.00000000]]\n", + "Result: Where(condition,X,Y)=[[1.00000000,2.00000000], [3.00000000,9.00000000], [8.00000000,6.00000000]]\n", + "\n", + "## Example 3a (IEEE-754)\n", + "condition=[[ True,False, True]]\n", + "X=[[19.00000000,28.00000000,37.09999847]]\n", + "Y=[[16.00000000,25.00000000,34.00000000]]\n", + "Result: Where(condition,X,Y)=[[19.00000000,25.00000000,37.09999847]]\n", + "\n", + "## Example 3b (IEEE-754 special values)\n", + "condition=[[ True,False, True,False, True]]\n", + "X=[[0.00000000,0.00000000, inf, inf, nan]]\n", + "Y=[[ 0.00000000,-0.00000000, -inf, -inf, 1.00000000]]\n", + "Result: Where(condition,X,Y)=[[ 0.00000000,-0.00000000, inf, -inf, nan]]\n", + "\n", + "## Example 4 (integer)\n", + "condition=[[ True, True], [ True,False], [False, True]]\n", + "X=[[ 1.00000000,20.00000000], [ 3.00000000,40.00000000], [ 5.00000000,60.00000000]]\n", + "Y=[[ 12.00000000,110.00000000], [ 10.00000000, 90.00000000], [ 8.00000000, 70.00000000]]\n", + "Result: Where(condition,X,Y)=[[ 1.00000000,20.00000000], [ 3.00000000,90.00000000], [ 8.00000000,60.00000000]]\n" + ] + } + ], + "source": [ + "import onnx\n", + "import onnxruntime as ort\n", + "import numpy as np\n", + "\n", + "# Define input and output tensor names\n", + "input_name_condition = \"condition\"\n", + "input_name_X = \"X\"\n", + "input_name_Y = \"Y\"\n", + "where_output_name = \"WhereOutput\"\n", + "\n", + "#-------------------------------------------------------------------------------\n", + "# 2-rank input tensors\n", + "#-------------------------------------------------------------------------------\n", + "\n", + "# Create 2-rank input tensors\n", + "input_tensor_condition = onnx.helper.make_tensor_value_info(\n", + " input_name_condition, onnx.TensorProto.BOOL, [None, None]\n", + ")\n", + "\n", + "input_tensor_X = onnx.helper.make_tensor_value_info(\n", + " input_name_X, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "input_tensor_Y = onnx.helper.make_tensor_value_info(\n", + " input_name_Y, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "# Create output tensor\n", + "where_output = onnx.helper.make_tensor_value_info(\n", + " where_output_name, onnx.TensorProto.FLOAT, [None, None]\n", + ")\n", + "\n", + "# Define Where node: Z = Where(condition, X, Y)\n", + "where_node = onnx.helper.make_node(\n", + " \"Where\",\n", + " [input_name_condition, input_name_X, input_name_Y],\n", + " [where_output_name]\n", + ")\n", + "\n", + "# Create the ONNX graph\n", + "graph = onnx.helper.make_graph(\n", + " nodes=[where_node],\n", + " name=\"Where\",\n", + " inputs=[input_tensor_condition, input_tensor_X, input_tensor_Y],\n", + " outputs=[where_output]\n", + ")\n", + "\n", + "# Create the ONNX model (Where available in opset 16)\n", + "model = onnx.helper.make_model(\n", + " graph,\n", + " opset_imports=[onnx.helper.make_opsetid(\"\", 16)]\n", + ")\n", + "\n", + "onnx.checker.check_model(model)\n", + "\n", + "# Save the model\n", + "onnx.save(model, \"where.onnx\")\n", + "\n", + "# Load and run the model using ONNX Runtime\n", + "session = ort.InferenceSession(\"where.onnx\")\n", + "\n", + "def do_where(cond, x, y):\n", + " output = session.run(\n", + " None,\n", + " {\n", + " input_name_condition: cond,\n", + " input_name_X: x,\n", + " input_name_Y: y,\n", + " },\n", + " )\n", + "\n", + " c_f = np.array2string(cond, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " x_f = np.array2string(x, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " y_f = np.array2string(y, separator=',', max_line_width=np.inf).replace('\\n', '')\n", + " o_f = np.array2string(output[0], separator=',', max_line_width=np.inf).replace('\\n', '')\n", + "\n", + " print(f\"condition={c_f}\")\n", + " print(f\"X={x_f}\")\n", + " print(f\"Y={y_f}\")\n", + " print(f\"Result: Where(condition,X,Y)={o_f}\")\n", + "\n", + "np.set_printoptions(precision=None, floatmode='fixed')\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Nominal cases\n", + "#--------------------------------------------------------------------\n", + "\n", + "# Example 1 (real/float section)\n", + "\n", + "print(\"\\n## Example 1\")\n", + "\n", + "condition_1 = np.array([[True, False, True]], dtype=bool)\n", + "\n", + "x_1 = np.array([[9.0, 8.0, 7.1]], dtype=np.float32)\n", + "y_1 = np.array([[6.0, 5.0, 4.0]], dtype=np.float32)\n", + "\n", + "do_where(condition_1, x_1, y_1)\n", + "\n", + "\n", + "# Example 2 (real section)\n", + "\n", + "print(\"\\n## Example 2\")\n", + "\n", + "condition_2 = np.array([\n", + " [True, True],\n", + " [True, False],\n", + " [False, True]\n", + "], dtype=bool)\n", + "\n", + "x_2 = np.array([\n", + " [1.0, 2.0],\n", + " [3.0, 4.0],\n", + " [5.0, 6.0]\n", + "], dtype=np.float32)\n", + "\n", + "y_2 = np.array([\n", + " [12.0, 11.0],\n", + " [10.0, 9.0],\n", + " [8.0, 7.0]\n", + "], dtype=np.float32)\n", + "\n", + "do_where(condition_2, x_2, y_2)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Floating-point special values \n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 3a (IEEE-754)\")\n", + "\n", + "condition_3 = np.array([[True, False, True]], dtype=bool)\n", + "\n", + "x_3 = np.array([[19.0, 28.0, 37.1]], dtype=np.float32)\n", + "\n", + "y_3 = np.array([[16.0, 25.0, 34.0]], dtype=np.float32)\n", + "\n", + "do_where(condition_3, x_3, y_3) \n", + "\n", + "#--------------------------------------------------------------------\n", + "# Floating-point special values (+0, -0, +inf, -inf, nan)\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 3b (IEEE-754 special values)\")\n", + "\n", + "condition_3 = np.array([[True, False, True, False, True]], dtype=bool)\n", + "\n", + "x_3 = np.array([[+0.0, +0.0, float(\"inf\"), float(\"inf\"), float(\"nan\")]], dtype=np.float32)\n", + "\n", + "y_3 = np.array([[+0.0, -0.0, float(\"-inf\"), float(\"-inf\"), 1.0]], dtype=np.float32)\n", + "\n", + "do_where(condition_3, x_3, y_3)\n", + "\n", + "\n", + "#--------------------------------------------------------------------\n", + "# Integer example\n", + "#--------------------------------------------------------------------\n", + "\n", + "print(\"\\n## Example 4 (integer)\")\n", + "\n", + "condition_4 = np.array([\n", + " [True, True],\n", + " [True, False],\n", + " [False, True]\n", + "], dtype=bool)\n", + "\n", + "x_4 = np.array([\n", + " [1, 20],\n", + " [3, 40],\n", + " [5, 60]\n", + "], dtype=np.int32)\n", + "\n", + "y_4 = np.array([\n", + " [12, 110],\n", + " [10, 90],\n", + " [8, 70]\n", + "], dtype=np.int32)\n", + "\n", + "do_where(condition_4, x_4.astype(np.float32), y_4.astype(np.float32))" + ] + } + ], + "metadata": { + "colab": { + "authorship_tag": "ABX9TyP+i8qgZsjFhcbLt3N58Sdn", + "include_colab_link": true, + "provenance": [] + }, + "kernelspec": { + "display_name": "Python (.venv)", + "language": "python", + "name": "venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.12" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/safety-related-profile/tools/req_generation/README.md b/safety-related-profile/tools/req_generation/README.md new file mode 100644 index 00000000..12edd750 --- /dev/null +++ b/safety-related-profile/tools/req_generation/README.md @@ -0,0 +1,17 @@ +# SONNX specification generator + +This application uses [Jinja2](https://pypi.org/project/Jinja2/) to generate the markdown specification of an operator defined in a YAML file. + +Install the dependencies: +``` +pip install -r requirements.txt +``` + +To generate a specification +- create the "op.yaml" file containing th definition of the operator +- then call +``` +python render_operator.py --metadata op.yaml --template operator_spec.md.j2 --output op.generated.md +``` + +See the example given for the [**Div**](./div.yaml) operator. diff --git a/safety-related-profile/tools/req_generation/div.generated.md b/safety-related-profile/tools/req_generation/div.generated.md new file mode 100644 index 00000000..0650a93f --- /dev/null +++ b/safety-related-profile/tools/req_generation/div.generated.md @@ -0,0 +1,433 @@ +Generated on: 2026-06-08 + +# Contents + +- Div operator for type [real](#real) +- Div operator for types [float16, float, double](#float) +- Div operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int) + +Based on ONNX documentation [Div version 14](https://onnx.ai/onnx/operators/onnx__Div.html#div-14). + + + +# Specification of operator Div for real values + +## Signature + +$$ +C = \textbf{Div}(A, B) +$$ + +where: +- $A$ (real): Numerator +- $B$ (real): Denominator +- $C$ (real): Element-wise division of $A$ by $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function + + +[E_DIV_REAL_FUNC_0010]
+Operator Div divides input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. + +If $i$ is a [tensor index](./../common/defs.md#tensor_index), each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/defs.md#tensor_index) $i$: + +$$ +C[i] = \frac{A[i]}{B[i]} +$$ + +if $B[i] \neq 0$; otherwise $C[i]$ is not defined. + +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 + +$$ +A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} +$$ + +$$ +B = \begin{bmatrix} 3 & 3.3 & 5.1 \end{bmatrix} +$$ + +$$ +C = \frac{A}{B} + = \begin{bmatrix} 6.1/3 & 9.5/3.3 & 35.7/5.1 \end{bmatrix} +$$ +### Example 2 + +$$ +A = +\begin{bmatrix} + 3.7 & 4.4 \\ + 16.2 & 0.5 \\ + 25.3 & 24.8 +\end{bmatrix} +$$ + +$$ +B = +\begin{bmatrix} + 3 & 2.2 \\ + 4.1 & 1 \\ + 5.2 & 4 +\end{bmatrix} +$$ + +$$ +C = \frac{A}{B} + = +\begin{bmatrix} + 3.7/3 & 2 \\ + 16.2/4.1 & 0.5 \\ + 25.3/5.2 & 6.2 +\end{bmatrix} +$$ + +## Error conditions + +No error condition. + +## Attributes + +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: real + +Numerator + +#### Constraints + + +- `[E_DIV_REAL_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ shall have the same shape. + +### $\text{B}$: real + +Denominator + +#### Constraints + + +- `[E_DIV_REAL_CONSTR_B_0010]` Shape consistency + - Statement: See constraint [E_DIV_REAL_CONSTR_A_0010](#E_DIV_REAL_CONSTR_A_0010) on tensor $A$. + +- `[E_DIV_REAL_CONSTR_B_0020]` Avoid undefined behaviour for operator Div + - Statement: $\forall i,\ B[i] \neq 0$ + +## Outputs + +### $\text{C}$: real + +Element-wise division of $A$ by $B$ + +#### Constraints + + +- `[E_DIV_REAL_CONSTR_C_0010]` Shape consistency + - Statement: See constraint [E_DIV_REAL_CONSTR_A_0010](#E_DIV_REAL_CONSTR_A_0010) on tensor $A$. + +--- + + +# Specification of operator Div for float values + +where float is in {float16, float, double} + +## Signature + +$$ +C = \textbf{Div}(A, B) +$$ + +where: +- $A$ (float): Numerator +- $B$ (float): Denominator +- $C$ (float): Element-wise division of $A$ by $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function + + +[E_DIV_FLOAT_FUNC_0010]
+Operator Div divides input tensors $A$ and $B$ element-wise according to IEEE 754 floating-point semantics and stores the result in output tensor $C$. + +If $i$ is a [tensor index](./../common/defs.md#tensor_index), each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$. + +The mathematical definition of the operator is given hereafter. + +For any [tensor index](./../common/defs.md#tensor_index) $i$: + +$$ +C[i] = +\begin{cases} +A[i]/B[i] & \text{if } A[i] \text{ and } B[i] \text{ are different from } 0 \\ +\pm\mathrm{inf} & \text{if } A[i] \neq 0 \text{ and } B[i] = 0 \\ +\mathrm{NaN} & \text{if } A[i] = 0 \text{ and } B[i] = 0 +\end{cases} +$$ + +In the second case, the sign of $\pm\mathrm{inf}$ is determined from the signs of $A[i]$ and the zero value ($+0$ or $-0$) according to IEEE 754 rules. + +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 + +$$ +A = +\begin{bmatrix} +3.0 & 4.5 \\ +16.0 & 1.0 \\ +25.5 & 24.25 +\end{bmatrix} +\quad +B = +\begin{bmatrix} +3.0 & 2.0 \\ +4.0 & 0.0 \\ +5.0 & 4.0 +\end{bmatrix} +$$ + +$$ +C \approx +\begin{bmatrix} +1.0 & 2.25 \\ +4.0 & +\mathrm{inf} \\ +5.1 & 6.0625 +\end{bmatrix} +$$ +### Example 2 + +$$ +A = +\begin{bmatrix} +3.25 & 4.5 \\ +16.0 & 0.0 \\ +25.5 & 24.25 +\end{bmatrix} +\quad +B = +\begin{bmatrix} +3.0 & 2.0 \\ +4.0 & 0.0 \\ +5.0 & 4.0 +\end{bmatrix} +$$ + +$$ +C \approx +\begin{bmatrix} +1.0833 & 2.25 \\ +4.0 & \mathrm{NaN} \\ +5.1 & 6.0625 +\end{bmatrix} +$$ + +## Error conditions + +- A value in the output tensor is NaN if the numerator and denominator are both 0 ($+0.0$ or $-0.0$ according to IEEE 754). + +## Attributes + +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: float + +Numerator + +#### Constraints + + +- `[E_DIV_FLOAT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same shape. + +- `[E_DIV_FLOAT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: float + +Denominator + +#### Constraints + + +- `[E_DIV_FLOAT_CONSTR_B_0010]` Shape consistency + - Statement: See constraint [E_DIV_FLOAT_CONSTR_A_0010](#E_DIV_FLOAT_CONSTR_A_0010) on tensor $A$. + +- `[E_DIV_FLOAT_CONSTR_B_0020]` Type consistency + - Statement: See constraint [E_DIV_FLOAT_CONSTR_A_0020](#E_DIV_FLOAT_CONSTR_A_0020) on tensor $A$. + +## Outputs + +### $\text{C}$: float + +Element-wise division of $A$ by $B$ + +#### Constraints + + +- `[E_DIV_FLOAT_CONSTR_C_0010]` Shape consistency + - Statement: See constraint [E_DIV_FLOAT_CONSTR_A_0010](#E_DIV_FLOAT_CONSTR_A_0010) on tensor $A$. + +- `[E_DIV_FLOAT_CONSTR_C_0020]` Type consistency + - Statement: See constraint [E_DIV_FLOAT_CONSTR_A_0020](#E_DIV_FLOAT_CONSTR_A_0020) on tensor $A$. + +--- + + +# Specification of operator Div for int values + +where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}. + +## Signature + +$$ +C = \textbf{Div}(A, B) +$$ + +where: +- $A$ (int): Numerator +- $B$ (int): Denominator +- $C$ (int): Element-wise division of $A$ by $B$ + +## Restrictions + +[General restrictions](./../common/general_restrictions.md) are applicable. + +No specific restrictions apply to the **Div** operator. + +## Function + + +[E_DIV_INT_FUNC_0010]
+Operator Div divides input tensors $A$ and $B$ element-wise and stores the result in output tensor $C$. + +For any [tensor index](./../common/defs.md#tensor_index) $i$, the result of the division is the algebraic quotient of $A[i]$ by $B[i]$ with any fractional part discarded. + +$$ +C[i] = \operatorname{trunc}\left(\frac{A[i]}{B[i]}\right) +$$ + +where $B[i] \neq 0$. + +[END]
+ +The effect of the operator is illustrated on the following examples. + +### Example 1 + +$$ +A = \begin{bmatrix} 6 & 5 & -35 \end{bmatrix} +\quad +B = \begin{bmatrix} 3 & 3 & 3 \end{bmatrix} +$$ + +$$ +C = \begin{bmatrix} 2 & 1 & -11 \end{bmatrix} +$$ +### Example 2 + +$$ +A = +\begin{bmatrix} +10 & 10 \\ +21 & 1 \\ +30 & 9 +\end{bmatrix} +\quad +B = +\begin{bmatrix} +3 & 2 \\ +4 & 1 \\ +5 & 4 +\end{bmatrix} +$$ + +$$ +C = +\begin{bmatrix} +3 & 5 \\ +5 & 1 \\ +6 & 2 +\end{bmatrix} +$$ + +## Error conditions + +- The behaviour in case of a null denominator is implementation dependent. + +## Attributes + +Operator **Div** has no attribute. + +## Inputs + +### $\text{A}$: int + +Numerator + +#### Constraints + + +- `[E_DIV_INT_CONSTR_A_0010]` Shape consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same shape. + +- `[E_DIV_INT_CONSTR_A_0020]` Type consistency + - Statement: Tensors $A$, $B$, and $C$ must have the same type. + +### $\text{B}$: int + +Denominator + +#### Constraints + + +- `[E_DIV_INT_CONSTR_B_0010]` Shape consistency + - Statement: See constraint [E_DIV_INT_CONSTR_A_0010](#E_DIV_INT_CONSTR_A_0010) on tensor $A$. + +- `[E_DIV_INT_CONSTR_B_0020]` Type consistency + - Statement: See constraint [E_DIV_INT_CONSTR_A_0020](#E_DIV_INT_CONSTR_A_0020) on tensor $A$. + +- `[E_DIV_INT_CONSTR_B_0030]` Definition domain + - Statement: $$ + \forall i,\ B[i] \neq 0 + $$ + +## Outputs + +### $\text{C}$: int + +Element-wise division of $A$ by $B$ + +#### Constraints + + +- `[E_DIV_INT_CONSTR_C_0010]` Shape consistency + - Statement: See constraint [E_DIV_INT_CONSTR_A_0010](#E_DIV_INT_CONSTR_A_0010) on tensor $A$. + +- `[E_DIV_INT_CONSTR_C_0020]` Type consistency + - Statement: See constraint [E_DIV_INT_CONSTR_A_0020](#E_DIV_INT_CONSTR_A_0020) on tensor $A$. + diff --git a/safety-related-profile/tools/req_generation/div.yaml b/safety-related-profile/tools/req_generation/div.yaml new file mode 100644 index 00000000..d9ac44ea --- /dev/null +++ b/safety-related-profile/tools/req_generation/div.yaml @@ -0,0 +1,387 @@ +#----------------------------------------------------------------------------- +# General definitions that can be used anywhere using {{ defs. }} +#----------------------------------------------------------------------------- + +defs: + op_name: "Div" + op_math: "\\textbf{Div}" + + input_a: "$A$" + input_b: "$B$" + output_c: "$C$" + + tensor_index: "[tensor index](./../common/defs.md#tensor_index)" + shape_consistency_title: "Shape consistency" + type_consistency_title: "Type consistency" + + # Used by the Python helper req_link("REQ_ID"). + # The placeholder {req_id} is replaced by the referenced requirement ID. + req_link_template: "[{req_id}](#{req_id})" + + +#----------------------------------------------------------------------------- +# Document-level data +#----------------------------------------------------------------------------- +document: + title: "{{ defs.op_name }} operator specification" + operator: "{{ defs.op_name }}" + onnx: + version: 14 + url: "https://onnx.ai/onnx/operators/onnx__Div.html#div-14" + general_restrictions_path: "./../common/general_restrictions.md" + + + +#----------------------------------------------------------------------------- +# TOC +#----------------------------------------------------------------------------- + +contents: + - label: "{{ defs.op_name }} operator for type [real](#real)" + - label: "{{ defs.op_name }} operator for types [float16, float, double](#float)" + - label: "{{ defs.op_name }} operator for types [int8, int16, int32, int64, uint8, uint16, uint32, uint64](#int)" + + +#----------------------------------------------------------------------------- +# Signature data +#----------------------------------------------------------------------------- +# +# Type rule: +# - if an argument defines `type` here, this type is reused for every section; +# - if an argument does not define `type` here, its type is generated from +# the section datatype, for example `real`, `float`, or `int`. +signature: + inputs: + - name: "A" + role: "Numerator." + - name: "B" + role: "Denominator." + outputs: + - name: "C" + role: "Element-wise division of {{ defs.input_a }} by {{ defs.input_b }}." + +#----------------------------------------------------------------------------- +# First variant +#----------------------------------------------------------------------------- +variants: + - datatype: "real" + + restrictions: + specific: [] + + function: + requirements: + - req_id: "E_DIV_REAL_FUNC_0010" + title: "Element-wise real division" + text: | + Operator {{ defs.op_name }} divides input tensors {{ defs.input_a }} and {{ defs.input_b }} element-wise and stores the result in output tensor {{ defs.output_c }}. + + If $i$ is a {{ defs.tensor_index }}, each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$. + + The mathematical definition of the operator is given hereafter. + + For any {{ defs.tensor_index }} $i$: + + $$ + C[i] = \frac{A[i]}{B[i]} + $$ + + if $B[i] \neq 0$; otherwise $C[i]$ is not defined. + + examples: + - title: "Example 1" + body: | + $$ + A = \begin{bmatrix} 6.1 & 9.5 & 35.7 \end{bmatrix} + $$ + + $$ + B = \begin{bmatrix} 3 & 3.3 & 5.1 \end{bmatrix} + $$ + + $$ + C = \frac{A}{B} + = \begin{bmatrix} 6.1/3 & 9.5/3.3 & 35.7/5.1 \end{bmatrix} + $$ + + - title: "Example 2" + body: | + $$ + A = + \begin{bmatrix} + 3.7 & 4.4 \\ + 16.2 & 0.5 \\ + 25.3 & 24.8 + \end{bmatrix} + $$ + + $$ + B = + \begin{bmatrix} + 3 & 2.2 \\ + 4.1 & 1 \\ + 5.2 & 4 + \end{bmatrix} + $$ + + $$ + C = \frac{A}{B} + = + \begin{bmatrix} + 3.7/3 & 2 \\ + 16.2/4.1 & 0.5 \\ + 25.3/5.2 & 6.2 + \end{bmatrix} + $$ + + error_conditions: [] + + attributes: [] + + constraints: + A: + - req_id: "E_DIV_REAL_CONSTR_A_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + Tensors {{ defs.input_a }}, {{ defs.input_b }}, and {{ defs.output_c }} shall have the same shape. + + B: + - req_id: "E_DIV_REAL_CONSTR_B_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_REAL_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_REAL_CONSTR_B_0020" + title: "Avoid undefined behaviour for operator {{ defs.op_name }}" + statement: | + $\forall i,\ B[i] \neq 0$ + + C: + - req_id: "E_DIV_REAL_CONSTR_C_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_REAL_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + + - datatype: "float" + type_note: "where float is in {float16, float, double}" + + restrictions: + specific: [] + + function: + requirements: + - req_id: "E_DIV_FLOAT_FUNC_0010" + title: "Element-wise floating-point division" + text: | + Operator {{ defs.op_name }} divides input tensors {{ defs.input_a }} and {{ defs.input_b }} element-wise according to IEEE 754 floating-point semantics and stores the result in output tensor {{ defs.output_c }}. + + If $i$ is a {{ defs.tensor_index }}, each element $C[i]$ is the result of dividing $A[i]$ by $B[i]$. + + The mathematical definition of the operator is given hereafter. + + For any {{ defs.tensor_index }} $i$: + + $$ + C[i] = + \begin{cases} + A[i]/B[i] & \text{if } A[i] \text{ and } B[i] \text{ are different from } 0 \\ + \pm\mathrm{inf} & \text{if } A[i] \neq 0 \text{ and } B[i] = 0 \\ + \mathrm{NaN} & \text{if } A[i] = 0 \text{ and } B[i] = 0 + \end{cases} + $$ + + In the second case, the sign of $\pm\mathrm{inf}$ is determined from the signs of $A[i]$ and the zero value ($+0$ or $-0$) according to IEEE 754 rules. + + examples: + - title: "Example 1" + body: | + $$ + A = + \begin{bmatrix} + 3.0 & 4.5 \\ + 16.0 & 1.0 \\ + 25.5 & 24.25 + \end{bmatrix} + \quad + B = + \begin{bmatrix} + 3.0 & 2.0 \\ + 4.0 & 0.0 \\ + 5.0 & 4.0 + \end{bmatrix} + $$ + + $$ + C \approx + \begin{bmatrix} + 1.0 & 2.25 \\ + 4.0 & +\mathrm{inf} \\ + 5.1 & 6.0625 + \end{bmatrix} + $$ + + - title: "Example 2" + body: | + $$ + A = + \begin{bmatrix} + 3.25 & 4.5 \\ + 16.0 & 0.0 \\ + 25.5 & 24.25 + \end{bmatrix} + \quad + B = + \begin{bmatrix} + 3.0 & 2.0 \\ + 4.0 & 0.0 \\ + 5.0 & 4.0 + \end{bmatrix} + $$ + + $$ + C \approx + \begin{bmatrix} + 1.0833 & 2.25 \\ + 4.0 & \mathrm{NaN} \\ + 5.1 & 6.0625 + \end{bmatrix} + $$ + + error_conditions: + - | + A value in the output tensor is NaN if the numerator and denominator are both 0 ($+0.0$ or $-0.0$ according to IEEE 754). + + attributes: [] + + constraints: + A: + - req_id: "E_DIV_FLOAT_CONSTR_A_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + Tensors {{ defs.input_a }}, {{ defs.input_b }}, and {{ defs.output_c }} must have the same shape. + - req_id: "E_DIV_FLOAT_CONSTR_A_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + Tensors {{ defs.input_a }}, {{ defs.input_b }}, and {{ defs.output_c }} must have the same type. + + B: + - req_id: "E_DIV_FLOAT_CONSTR_B_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_FLOAT_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_FLOAT_CONSTR_B_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_FLOAT_CONSTR_A_0020") }} on tensor {{ defs.input_a }}. + + C: + - req_id: "E_DIV_FLOAT_CONSTR_C_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_FLOAT_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_FLOAT_CONSTR_C_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_FLOAT_CONSTR_A_0020") }} on tensor {{ defs.input_a }}. + + - datatype: "int" + type_note: "where int is in {int8, int16, int32, int64, uint8, uint16, uint32, uint64}." + + restrictions: + specific: [] + + function: + requirements: + - req_id: "E_DIV_INT_FUNC_0010" + title: "Element-wise integer division" + text: | + Operator {{ defs.op_name }} divides input tensors {{ defs.input_a }} and {{ defs.input_b }} element-wise and stores the result in output tensor {{ defs.output_c }}. + + For any {{ defs.tensor_index }} $i$, the result of the division is the algebraic quotient of $A[i]$ by $B[i]$ with any fractional part discarded. + + $$ + C[i] = \operatorname{trunc}\left(\frac{A[i]}{B[i]}\right) + $$ + + where $B[i] \neq 0$. + + examples: + - title: "Example 1" + body: | + $$ + A = \begin{bmatrix} 6 & 5 & -35 \end{bmatrix} + \quad + B = \begin{bmatrix} 3 & 3 & 3 \end{bmatrix} + $$ + + $$ + C = \begin{bmatrix} 2 & 1 & -11 \end{bmatrix} + $$ + + - title: "Example 2" + body: | + $$ + A = + \begin{bmatrix} + 10 & 10 \\ + 21 & 1 \\ + 30 & 9 + \end{bmatrix} + \quad + B = + \begin{bmatrix} + 3 & 2 \\ + 4 & 1 \\ + 5 & 4 + \end{bmatrix} + $$ + + $$ + C = + \begin{bmatrix} + 3 & 5 \\ + 5 & 1 \\ + 6 & 2 + \end{bmatrix} + $$ + + error_conditions: + - "The behaviour in case of a null denominator is implementation dependent." + + attributes: [] + + constraints: + A: + - req_id: "E_DIV_INT_CONSTR_A_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + Tensors {{ defs.input_a }}, {{ defs.input_b }}, and {{ defs.output_c }} must have the same shape. + - req_id: "E_DIV_INT_CONSTR_A_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + Tensors {{ defs.input_a }}, {{ defs.input_b }}, and {{ defs.output_c }} must have the same type. + + B: + - req_id: "E_DIV_INT_CONSTR_B_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_INT_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_INT_CONSTR_B_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_INT_CONSTR_A_0020") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_INT_CONSTR_B_0030" + title: "Definition domain" + statement: | + $$ + \forall i,\ B[i] \neq 0 + $$ + + C: + - req_id: "E_DIV_INT_CONSTR_C_0010" + title: "{{ defs.shape_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_INT_CONSTR_A_0010") }} on tensor {{ defs.input_a }}. + - req_id: "E_DIV_INT_CONSTR_C_0020" + title: "{{ defs.type_consistency_title }}" + statement: | + See constraint {{ req_link("E_DIV_INT_CONSTR_A_0020") }} on tensor {{ defs.input_a }}. diff --git a/safety-related-profile/tools/req_generation/operator_spec.md.j2 b/safety-related-profile/tools/req_generation/operator_spec.md.j2 new file mode 100644 index 00000000..ec4e2b34 --- /dev/null +++ b/safety-related-profile/tools/req_generation/operator_spec.md.j2 @@ -0,0 +1,131 @@ +Generated on: {{ generation.date }} + +# Contents + +{% for item in contents -%} +- {{ item.label }} +{% endfor %} + +Based on ONNX documentation [{{ document.operator }} version {{ document.onnx.version }}]({{ document.onnx.url }}). + +{% for variant in variants %} +{% if not loop.first %} +--- +{% endif %} + + +# {{ variant.title }} + +{% set type_note = variant.get("type_note", "") %} +{% if type_note %} +{{ type_note }} + +{% endif %} +## Signature + +{{ variant.signature.expression }} + +where: +{% for arg in variant.signature.inputs %} +- ${{ arg.name }}$ ({{ arg.type }}): {{ arg.role }} +{% endfor %} +{% for out in variant.signature.outputs %} +- ${{ out.name }}$ ({{ out.type }}): {{ out.role }} +{% endfor %} + +## Restrictions + +[General restrictions]({{ document.general_restrictions_path }}) are applicable. + +{% if variant.restrictions.specific %} +{% for restriction in variant.restrictions.specific %} +- {{ restriction }} +{% endfor %} +{% else %} +No specific restrictions apply to the **{{ document.operator }}** operator. +{% endif %} + +## Function + +{% for req in variant.function.requirements %} + +[{{ req.req_id }}]
+{{ req.text }} + +[END]
+{% endfor %} + +{% set examples = variant.get("examples", []) %} +{% if examples %} +The effect of the operator is illustrated on the following examples. + +{% for example in examples %} +### {{ example.title }} + +{{ example.body }} +{% endfor %} +{% endif %} + +## Error conditions + +{% set error_conditions = variant.get("error_conditions", []) %} +{% if error_conditions %} +{% for condition in error_conditions %} +- {{ condition }} +{% endfor %} +{% else %} +No error condition. +{% endif %} + +## Attributes + +{% set attributes = variant.get("attributes", []) %} +{% if attributes %} +{% for attr in attributes %} +### {{ attr.name }} + +{{ attr.role }} +{% endfor %} +{% else %} +Operator **{{ document.operator }}** has no attribute. +{% endif %} + +## Inputs + +{% for arg in variant.signature.inputs %} +### $\text{ {{- arg.name -}} }$: {{ arg.type }} + +{{ arg.role }} + +{% set arg_constraints = variant.constraints.get(arg.name, []) %} +{% if arg_constraints %} +#### Constraints + +{% for constraint in arg_constraints %} + +- `[{{ constraint.req_id }}]` {{ constraint.title }} + - Statement: {{ constraint.statement | indent(4) }} +{% endfor %} +{% endif %} + +{% endfor %} +## Outputs + +{% for out in variant.signature.outputs %} +### $\text{ {{- out.name -}} }$: {{ out.type }} + +{{ out.role }} + +{% set out_constraints = variant.constraints.get(out.name, []) %} +{% if out_constraints %} +#### Constraints + +{% for constraint in out_constraints %} + +- `[{{ constraint.req_id }}]` {{ constraint.title }} + - Statement: {{ constraint.statement | indent(4) }} +{% endfor %} +{% endif %} + +{% endfor %} +{% endfor %} diff --git a/safety-related-profile/tools/req_generation/render_operator.py b/safety-related-profile/tools/req_generation/render_operator.py new file mode 100644 index 00000000..f6ce730d --- /dev/null +++ b/safety-related-profile/tools/req_generation/render_operator.py @@ -0,0 +1,544 @@ +#!/usr/bin/env python3 +""" +Render an operator specification from YAML metadata and a Jinja template. + +Pipeline: + +1. Metadata rendering: + Jinja expressions inside YAML strings are resolved first. + Example: + "Avoid undefined behaviour for operator {{ defs.op_name }}" + becomes: + "Avoid undefined behaviour for operator **Div**" + +2. Metadata enrichment: + The script expands the common top-level signature into each variant. + The signature is defined only once in the YAML file: + + signature: + inputs: + - name: A + role: numerator + outputs: + - name: C + role: result + + Type rule: + - if an argument defines `type` in the common signature, that type is reused + in every variant; + - if an argument has no common `type`, the type is generated from the + variant datatype, for example `real`, `float`, or `int`. + + The script also generates: + - the mathematical signature expression from the expanded signature; + - the section title from the operator name and expanded argument types. + + Requirement IDs are NOT generated. They must be entered by the user + as `req_id` in the YAML file. + +3. Validation: + The script checks that every functional requirement and every constraint has + a `req_id`, and that all `req_id` values are unique. + +4. Document rendering: + The resolved and enriched metadata is passed to the Markdown Jinja template. + +""" + +from __future__ import annotations + +import argparse +import copy +from pathlib import Path +from typing import Any + +import yaml +from jinja2 import Environment, FileSystemLoader, StrictUndefined + +from datetime import datetime, timezone + +DEFAULT_REQ_LINK_TEMPLATE = ( + "[" + "{req_id}](#{req_id})" +) + +def add_generation_metadata(metadata: dict[str, Any]) -> dict[str, Any]: + """ + Add generation metadata to the document. + + The generated date is computed at rendering time. + """ + result = copy.deepcopy(metadata) + + generation = result.setdefault("generation", {}) + now = datetime.now(timezone.utc) + + generation["date"] = now.strftime("%Y-%m-%d") + generation["datetime_utc"] = now.strftime("%Y-%m-%d %H:%M:%S UTC") + generation["iso_datetime_utc"] = now.isoformat() + + return result + + +def make_req_link(req_id: str, metadata: dict[str, Any]) -> str: + """ + Return a Markdown/HTML hyperlink to a requirement or constraint. + + The hyperlink format can be customized in YAML: + + definitions: + req_link_template: "...{req_id}..." + """ + template = ( + metadata.get("definitions", {}).get("req_link_template") + or DEFAULT_REQ_LINK_TEMPLATE + ) + return str(template).format(req_id=req_id) + + +def make_metadata_environment(metadata: dict[str, Any]) -> Environment: + """ + Build the Jinja environment used to render expressions inside YAML metadata. + """ + env = Environment( + undefined=StrictUndefined, + autoescape=False, + trim_blocks=False, + lstrip_blocks=False, + ) + + # Helper available in YAML strings: + # {{ req_link("E_DIV_FLOAT_CONSTR_A_0010") }} + env.globals["req_link"] = lambda req_id: make_req_link(str(req_id), metadata) + + return env + + +def render_metadata_value(value: Any, env: Environment, context: dict[str, Any]) -> Any: + """ + Recursively render every string inside a metadata object. + """ + if isinstance(value, str): + return env.from_string(value).render(**context) + + if isinstance(value, list): + return [render_metadata_value(item, env, context) for item in value] + + if isinstance(value, dict): + return { + key: render_metadata_value(item, env, context) + for key, item in value.items() + } + + return value + + +def render_metadata(metadata: dict[str, Any]) -> dict[str, Any]: + """ + Render Jinja expressions inside the YAML metadata. + + The context includes the whole metadata tree. Definitions are accessed with: + + {{ defs.my_def }} + """ + result = copy.deepcopy(metadata) + env = make_metadata_environment(result) + return render_metadata_value(result, env, result) + + +def get_operator_math(metadata: dict[str, Any]) -> str: + """ + Return the operator representation used in math signatures. + + Preferred source: + defs.op_math + + Fallback: + document.operator + """ + definitions = metadata.get("defs", {}) + if definitions.get("op_math"): + return str(definitions["op_math"]) + return str(metadata["document"]["operator"]) + + +def get_operator_display(metadata: dict[str, Any]) -> str: + """ + Return the operator representation used in human-readable titles. + + Preferred source: + defs.op_name + + Fallback: + document.operator + """ + definitions = metadata.get("definitions", {}) + if definitions.get("op_name"): + return str(definitions["op_name"]) + return str(metadata["document"]["operator"]) + + +def unique_preserve_order(values: list[str]) -> list[str]: + """ + Return unique values while preserving first occurrence order. + """ + seen: set[str] = set() + result: list[str] = [] + + for value in values: + if value not in seen: + result.append(value) + seen.add(value) + + return result + + +def join_human_list(items: list[str]) -> str: + """ + Join a list in a readable form. + + [] -> "" + ["real"] -> "real" + ["real", "int"] -> "real and int" + ["real", "float", "int"] -> "real, float, and int" + """ + if not items: + return "" + + if len(items) == 1: + return items[0] + + if len(items) == 2: + return f"{items[0]} and {items[1]}" + + return ", ".join(items[:-1]) + f", and {items[-1]}" + + +def get_variant_datatype(variant: dict[str, Any]) -> str: + """ + Return the datatype associated with a variant section. + """ + if "datatype" not in variant: + raise ValueError( + f"All variants must have a `datatype`. " + ) + + return str(variant["datatype"]) + + +def apply_type_rule(argument: dict[str, Any], variant: dict[str, Any]) -> dict[str, Any]: + """ + Return a copy of a common-signature argument with the type rule applied. + + If the common signature argument defines `type`, it is reused. + Otherwise, the type is generated from variant.datatype. + """ + result = copy.deepcopy(argument) + + if not result.get("type"): + result["type"] = get_variant_datatype(variant) + + return result + + +def expand_common_signature( + common_signature: dict[str, Any], + variant: dict[str, Any], +) -> dict[str, Any]: + """ + Create a variant-specific signature from the common top-level signature. + """ + inputs = common_signature.get("inputs", []) + outputs = common_signature.get("outputs", []) + + if not inputs: + raise ValueError("Top-level signature has no inputs") + if not outputs: + raise ValueError("Top-level signature has no outputs") + + return { + "inputs": [apply_type_rule(arg, variant) for arg in inputs], + "outputs": [apply_type_rule(out, variant) for out in outputs], + } + + +def get_signature_types(variant: dict[str, Any]) -> list[str]: + """ + Return the unique types found in expanded signature inputs and outputs. + """ + signature = variant.get("signature", {}) + arguments = signature.get("inputs", []) + signature.get("outputs", []) + + types = [] + for item in arguments: + if "type" not in item: + raise ValueError( + f"Signature argument {item.get('name', '')} " + f"in variant {variant.get('id')} has no type" + ) + types.append(str(item["type"])) + + return unique_preserve_order(types) + + +def generate_variant_title(metadata: dict[str, Any], variant: dict[str, Any]) -> str: + """ + Generate a section title from the operator display name and signature types. + """ + operator_display = get_operator_display(metadata) + types_text = join_human_list(get_signature_types(variant)) + + if not types_text: + raise ValueError(f"Variant {variant.get('id')} has no signature types") + + return f"Specification of operator {operator_display} for {types_text} values" + + +def generate_signature_expression(metadata: dict[str, Any], variant: dict[str, Any]) -> str: + """ + Generate a display-math signature from signature.inputs and signature.outputs. + + Example with one output: + C = \\textbf{Div}(A, B) + + Example with several outputs: + A, B = \\textbf{MyOp}(X, Y, Z) + """ + signature = variant.get("signature", {}) + + inputs = signature.get("inputs", []) + outputs = signature.get("outputs", []) + + if not inputs: + raise ValueError(f"Variant {variant.get('id')} has no signature.inputs") + if not outputs: + raise ValueError(f"Variant {variant.get('id')} has no signature.outputs") + + input_names = ", ".join(str(item["name"]) for item in inputs) + output_names = ", ".join(str(item["name"]) for item in outputs) + operator_math = get_operator_math(metadata) + + return f"$$\n{output_names} = {operator_math}({input_names})\n$$" + + +def enrich_variants( + metadata: dict[str, Any], + overwrite_signature: bool = True, + overwrite_title: bool = True, +) -> dict[str, Any]: + """ + Add expanded variant.signature, generated signature.expression, and + generated variant.title to each variant. + + The expanded signature always starts from the top-level common signature. + """ + result = copy.deepcopy(metadata) + + if "signature" not in result: + raise ValueError("The YAML file must define a top-level `signature`") + + common_signature = result["signature"] + + for variant in result.get("variants", []): + if overwrite_signature or "signature" not in variant: + variant["signature"] = expand_common_signature(common_signature, variant) + else: + # Even when keeping a manual signature, ensure missing argument types + # are completed consistently. + variant["signature"] = expand_common_signature(variant["signature"], variant) + + variant["signature"]["expression"] = generate_signature_expression(result, variant) + + if overwrite_title or "title" not in variant: + variant["title"] = generate_variant_title(result, variant) + + return result + + +def iter_requirement_objects(metadata: dict[str, Any]): + """ + Yield all objects that must carry a req_id. + """ + for variant in metadata.get("variants", []): + variant_id = variant.get("id", "") + + for index, req in enumerate( + variant.get("function", {}).get("requirements", []), + start=1, + ): + yield ( + f"variants[{variant_id}].function.requirements[{index}]", + req, + ) + + for entity_name, constraints in variant.get("constraints", {}).items(): + for index, constraint in enumerate(constraints, start=1): + yield ( + f"variants[{variant_id}].constraints[{entity_name}][{index}]", + constraint, + ) + + +def assert_req_ids_present(metadata: dict[str, Any]) -> None: + """ + Fail early if any requirement or constraint lacks req_id. + """ + missing = [ + location + for location, item in iter_requirement_objects(metadata) + if not item.get("req_id") + ] + + if missing: + raise ValueError( + "Missing req_id for the following requirement/constraint entries:\n" + + "\n".join(f"- {location}" for location in missing) + ) + + +def collect_req_ids(metadata: dict[str, Any]) -> list[str]: + """ + Collect all user-entered requirement and constraint IDs. + """ + return [ + str(item["req_id"]) + for _, item in iter_requirement_objects(metadata) + if item.get("req_id") + ] + + +def assert_unique_req_ids(metadata: dict[str, Any]) -> None: + """ + Fail early if duplicate req_id values exist. + """ + seen: set[str] = set() + duplicates: set[str] = set() + + for req_id in collect_req_ids(metadata): + if req_id in seen: + duplicates.add(req_id) + seen.add(req_id) + + if duplicates: + raise ValueError( + "Duplicate req_id values found: " + + ", ".join(sorted(duplicates)) + ) + + +def render_document( + metadata: dict[str, Any], + template_path: Path, + output_path: Path, +) -> None: + """ + Render the final Markdown document from resolved metadata. + """ + env = Environment( + loader=FileSystemLoader(template_path.parent), + undefined=StrictUndefined, + autoescape=False, + trim_blocks=True, + lstrip_blocks=True, + ) + + # Also expose req_link in the final document template, in case the template + # itself needs to create requirement links. + env.globals["req_link"] = lambda req_id: make_req_link(str(req_id), metadata) + + template = env.get_template(template_path.name) + rendered = template.render(**metadata) + + output_path.write_text(rendered, encoding="utf-8") + + +def load_yaml(path: Path) -> dict[str, Any]: + with path.open("r", encoding="utf-8") as f: + data = yaml.safe_load(f) + + if not isinstance(data, dict): + raise TypeError(f"Expected a YAML mapping at top level in {path}") + + return data + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Render an operator specification from YAML and Jinja." + ) + parser.add_argument( + "--metadata", + type=Path, + default=Path("div.yaml"), + help="Path to the operator YAML metadata file.", + ) + parser.add_argument( + "--template", + type=Path, + default=Path("operator_spec.md.j2"), + help="Path to the Markdown Jinja template.", + ) + parser.add_argument( + "--output", + type=Path, + default=Path("div.generated.md"), + help="Path to the generated Markdown output file.", + ) + parser.add_argument( + "--resolved-metadata", + type=Path, + default=None, + help="Optional path where the resolved metadata is written.", + ) + parser.add_argument( + "--keep-manual-signature", + action="store_true", + help=( + "Keep variant-level manual signatures if they exist. " + "By default all variant signatures are generated from the top-level signature." + ), + ) + parser.add_argument( + "--keep-manual-title", + action="store_true", + help=( + "Do not overwrite variant.title when it already exists. " + "By default titles are always regenerated from signature types." + ), + ) + + args = parser.parse_args() + + metadata = load_yaml(args.metadata) + + # Phase 1: resolve expressions such as {{ defs.op_name }} inside YAML. + metadata = render_metadata(metadata) + + # Phase 2: enrich metadata. + metadata = enrich_variants( + metadata, + overwrite_signature=not args.keep_manual_signature, + overwrite_title=not args.keep_manual_title, + ) + + metadata = add_generation_metadata(metadata) + + # Phase 3: validate user-provided req_id values. + assert_req_ids_present(metadata) + assert_unique_req_ids(metadata) + + # Optional: save the fully resolved YAML. + if args.resolved_metadata is not None: + args.resolved_metadata.write_text( + yaml.safe_dump(metadata, sort_keys=False, allow_unicode=True), + encoding="utf-8", + ) + + # Phase 4: render the final Markdown specification. + render_document(metadata, args.template, args.output) + + print(f"Generated {args.output}") + + +if __name__ == "__main__": + main() diff --git a/safety-related-profile/tools/req_generation/requirements.txt b/safety-related-profile/tools/req_generation/requirements.txt new file mode 100644 index 00000000..5078e84d --- /dev/null +++ b/safety-related-profile/tools/req_generation/requirements.txt @@ -0,0 +1,2 @@ +Jinja2 +pyyaml \ No newline at end of file diff --git a/safety-related-profile/tools/why3_faq.md b/safety-related-profile/tools/why3_faq.md new file mode 100644 index 00000000..5dbef3ef --- /dev/null +++ b/safety-related-profile/tools/why3_faq.md @@ -0,0 +1,104 @@ + +# Installation + +## Why3Find Installation Guide + +This guide sets up a full OCaml + Why3 + Alt-Ergo + `why3find` environment on **Ubuntu/Debian-based systems**. + +--- + +## 🛠️ 1. Install System Dependencies + +```bash +sudo apt-get update +sudo apt-get install -y \ + build-essential \ + curl \ + git \ + m4 \ + unzip \ + bubblewrap \ + pkg-config \ + libgmp-dev \ + libzmq3-dev \ + zlib1g-dev \ + libexpat1-dev \ + libgtk-3-dev \ + libgtksourceview-3.0-dev +``` + +--- + +## 🐫 2. Install OPAM & Initialize OCaml Environment + +```bash +sudo apt-get install -y opam + +opam init --disable-sandboxing -y +eval $(opam env) + +opam switch create 4.13.0 +eval $(opam env) +``` + +--- + +## 📦 3. Install OCaml Packages + +```bash +opam install -y \ + "dune>=3.12" \ + "dune-site>=3.12" \ + "why3=1.8.0" \ + "yojson>=1.7.0" \ + "zmq>=5.0.0" \ + "terminal_size>=0.2.0" +``` + +--- + +## 🔍 4. Install `why3find` + +### Option A: From OPAM + +```bash +opam install why3find +``` + +### Option B: From Git Repository + +```bash +git clone https://git.frama-c.com/pub/why3find.git +cd why3find +opam install . +``` + +--- + +## 🧪 5. Install Alt-Ergo with Tests + +```bash +opam install -y alt-ergo=2.4.2 --with-test +``` + +--- + +## 📚 6. Install `odoc` with Documentation + +```bash +opam install -y odoc --with-doc +``` + +--- + +## 🧹 7. Install VSCode Extension for Why3 Platform + +```bash +wget https://git.frama-c.com/pub/why3find/-/jobs/1177337/artifacts/raw/vscode/why3-platform-1.1.1.vsix +code --install-extension why3-platform-1.1.1.vsix +``` + + + + +