Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f0e9cac
Implement JSONPath
markborkum Sep 16, 2018
7b10e61
Fix some Flake8 warnings
markborkum Sep 17, 2018
1ba83ad
Merge branch 'master' into feature/implement-jsonpath
dmlb2000 Sep 17, 2018
67f23e8
Initial pre-commit run
dmlb2000 Sep 17, 2018
11fe47e
Test parse_str method.
markborkum Sep 18, 2018
9d41884
Operator callables should be static
markborkum Sep 18, 2018
8748609
pre-commit fixes
dmlb2000 Sep 18, 2018
1af8e0e
add some bookstore examples for testing
dmlb2000 Sep 18, 2018
3abd07d
pre-commit fixes
dmlb2000 Sep 18, 2018
6976755
Rollback modification to grammar
markborkum Sep 18, 2018
f50eb52
Fix bookstore tests
markborkum Sep 18, 2018
29bb997
add more testing to increase coverage
dmlb2000 Sep 18, 2018
c936f12
pre-commit fix
dmlb2000 Sep 18, 2018
df8f033
more testing coverage
dmlb2000 Sep 18, 2018
b838d41
Improve slice notation
markborkum Sep 18, 2018
8881cde
Tests to increase coverage
markborkum Sep 19, 2018
83e1f6f
pre-commit fixes
markborkum Sep 19, 2018
a59b3e8
fix pre-commit
dmlb2000 Sep 19, 2018
d10b742
try some more non-sense tests
dmlb2000 Sep 19, 2018
91325b9
add array to test
dmlb2000 Sep 19, 2018
6759110
Python 3.5 doesn't seem to be working great
dmlb2000 Sep 19, 2018
0c8841a
Add coverage pragmas
markborkum Sep 19, 2018
c13bf7b
More coverage tests
markborkum Sep 19, 2018
6550b4f
try getting pre-commit and testing right
dmlb2000 Sep 19, 2018
c6458ca
subscriptable JSONPaths
markborkum Mar 7, 2019
e553d85
Merge branch 'master' into feat-subscriptable-jsonpath
markborkum Mar 7, 2019
c83cccf
Resolve flake8 E902, F401, F601 warnings
markborkum Mar 7, 2019
f8b461d
Resolve pep257 warnings
markborkum Mar 7, 2019
7b6648f
Resolve pep257 warning
markborkum Mar 7, 2019
54af051
Resolve autopep8 warnings
markborkum Mar 7, 2019
0d0ee96
Resolve pylint warnings
markborkum Mar 7, 2019
60e927a
Resolve flake8 F821 warning
markborkum Mar 7, 2019
789609a
Resolve pylint warnings
markborkum Mar 7, 2019
8951504
Resolve autopep8 warnings
markborkum Mar 7, 2019
4c3b742
Resolve pylint warnings (hopefuly...)
markborkum Mar 7, 2019
cd53e59
Array index subscript for integers only
markborkum Mar 7, 2019
af80c20
Rename variable
markborkum Mar 7, 2019
42ce2c7
Golf down __jsonpath__ implementation
markborkum Mar 7, 2019
cddedee
Rename subscript: "path" to "node"
markborkum Mar 7, 2019
5f4f817
Update README.md
markborkum Mar 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ This repository contains an implementation of [JSONPath](http://goessner.net/art

### `Path` class

The `jsonpath2.Path.Path` class represents a JSONPath.
The `jsonpath2.path.Path` class represents a JSONPath.

```python
>>> s = '{"hello":"Hello, world!"}'
Expand All @@ -24,7 +24,7 @@ The `jsonpath2.Path.Path` class represents a JSONPath.
['$["hello"]']
```

This class is constructed with respect to the given instance of the `jsonpath2.Path.RootNode` class (viz., the `root_node` property).
This class is constructed with respect to the given instance of the `jsonpath2.nodes.root.RootNode` class (viz., the `root_node` property).

#### `parse_str(strdata)` class method

Expand All @@ -37,7 +37,7 @@ Parse the contents of the given file and return a new instance of this class.
#### `match(root_value)` instance method

Match the given JSON data structure against this instance.
For each match, yield an instance of the `jsonpath2.Node.MatchData` class.
For each match, yield an instance of the `jsonpath2.node.MatchData` class.

#### `__eq__(other)` instance method

Expand All @@ -53,7 +53,7 @@ The root node of the abstract syntax tree for this instance.

### `Node` abstract class

The `jsonpath2.Node.Node` class represents the abstract syntax tree for a JSONPath.
The `jsonpath2.node.Node` class represents the abstract syntax tree for a JSONPath.

#### `__eq__(other)` instance method

Expand All @@ -66,15 +66,15 @@ Yields the lexer tokens for the string representation of this instance.
#### `match(root_value, current_value)` instance method

Match the given root and current JSON data structures against this instance.
For each match, yield an instance of the `jsonpath2.Node.MatchData` class.
For each match, yield an instance of the `jsonpath2.node.MatchData` class.

#### `tojsonpath()` instance method

Returns the string representation of this instance.

### `MatchData` class

The `jsonpath2.Node.MatchData` class represents the JSON value and context for a JSONPath match.
The `jsonpath2.node.MatchData` class represents the JSON value and context for a JSONPath match.

This class is constructed with respect to a root JSON value, a current JSON value, and an abstract syntax tree node.

Expand Down Expand Up @@ -111,7 +111,7 @@ The abstract syntax tree node.
| JSONPath Filter Expression | Description |
| - | - |
| `$` or `@` | nested JSONPath (returns `true` if any match exists; otherwise, returns `false`) |
| `=`, `!=`, `>`, `>=`, `<`, `<=` | binary operator, where left-hand operand is a nested JSONPath and right-right operand is a JSON value (returns `true` if any match exists; otherwise, returns `false`) |
| `=`, `!=`, `>`, `>=`, `<`, `<=` | binary operator, where left- and right-hand operands are nested JSONPaths or JSON values (returns `true` if any match exists; otherwise, returns `false`) |
| `and`, `or`, `not` | Boolean operator, where operands are JSONPath filter expressions |
| `(` ... `)` | parentheses |

Expand Down
56 changes: 42 additions & 14 deletions jsonpath2/expressions/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
"""The operator expression module."""
import json
from typing import Callable, Generator, List
from typing import Callable, Generator, List, Union
from jsonpath2.expression import Expression
from jsonpath2.node import Node

Expand All @@ -22,33 +22,61 @@ def evaluate(self, root_value: object, current_value: object) -> bool: # pragma
class BinaryOperatorExpression(OperatorExpression):
"""Binary operator expression."""

def __init__(self, token: str, callback: Callable[[object, object], bool], left_node: Node, right_value: object):
def __init__(self, token: str, callback: Callable[[object, object], bool],
left_node_or_value: Union[Node, object], right_node_or_value: Union[Node, object]):
"""Constructor save the left right and token."""
super(BinaryOperatorExpression, self).__init__()
self.token = token
self.callback = callback
self.left_node = left_node
self.right_value = right_value
self.left_node_or_value = left_node_or_value
self.right_node_or_value = right_node_or_value

def __jsonpath__(self) -> Generator[str, None, None]:
"""Return the string json path of this expression."""
for left_node_token in self.left_node.__jsonpath__():
yield left_node_token
if isinstance(self.left_node_or_value, Node):
for left_node_token in self.left_node_or_value.__jsonpath__():
yield left_node_token
else:
yield json.dumps(self.left_node_or_value)

yield ' '
yield self.token
yield ' '
yield json.dumps(self.right_value)

if isinstance(self.right_node_or_value, Node):
for right_node_token in self.right_node_or_value.__jsonpath__():
yield right_node_token
else:
yield json.dumps(self.right_node_or_value)

def evaluate(self, root_value: object, current_value: object) -> bool:
"""Evaluate the left and right values given the token."""
return any(
map(
lambda left_node_match_data: self.callback(
left_node_match_data.current_value,
self.right_value
),
self.left_node.match(root_value, current_value)
if isinstance(self.left_node_or_value, Node):
left_values = (
left_node_match_data.current_value
for left_node_match_data
in self.left_node_or_value.match(root_value, current_value)
)
else:
left_values = [
self.left_node_or_value,
]

if isinstance(self.right_node_or_value, Node):
right_values = (
right_node_match_data.current_value
for right_node_match_data
in self.right_node_or_value.match(root_value, current_value)
)
else:
right_values = [
self.right_node_or_value,
]

return any(
self.callback(left_value, right_value)
for left_value in left_values
for right_value in right_values
)


Expand Down
19 changes: 12 additions & 7 deletions jsonpath2/expressions/some.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""Some expression module."""
from typing import Generator
import json
from typing import Generator, Union
from jsonpath2.expression import Expression
from jsonpath2.node import Node


class SomeExpression(Expression):
"""The some expression class."""

def __init__(self, next_node: Node):
def __init__(self, next_node_or_value: Union[Node, object]):
"""Save the next node."""
super(SomeExpression, self).__init__()
self.next_node = next_node
self.next_node_or_value = next_node_or_value

def __jsonpath__(self) -> Generator[str, None, None]:
"""Return the next nodes jsonpath."""
return self.next_node.__jsonpath__()
if isinstance(self.next_node_or_value, Node):
return self.next_node_or_value.__jsonpath__()
return [json.dumps(self.next_node_or_value)]

def evaluate(self, root_value: object, current_value: object) -> bool:
"""Evaluate the next node."""
for _next_node_match_data in self.next_node.match(root_value, current_value):
return True
return False
if isinstance(self.next_node_or_value, Node):
for _next_node_match_data in self.next_node_or_value.match(root_value, current_value):
return True
return False
return bool(self.next_node_or_value)
13 changes: 12 additions & 1 deletion jsonpath2/parser/JSONPath.g4
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,16 @@ jsonpath
: ROOT_VALUE subscript? EOF
;

jsonpath_
: ( ROOT_VALUE | CURRENT_VALUE ) subscript?
;

jsonpath__
: jsonpath_
| value
;


subscript
: RECURSIVE_DESCENT ( subscriptableBareword | subscriptables ) subscript?
| SUBSCRIPT subscriptableBareword subscript?
Expand All @@ -55,6 +65,7 @@ subscriptable
| sliceable
| WILDCARD_SUBSCRIPT
| QUESTION PAREN_LEFT expression PAREN_RIGHT
| jsonpath_
;

sliceable
Expand All @@ -76,7 +87,7 @@ orExpression
notExpression
: NOT notExpression
| PAREN_LEFT expression PAREN_RIGHT
| ( ROOT_VALUE | CURRENT_VALUE ) subscript? ( ( EQ | NE | LT | LE | GT | GE ) value )?
| jsonpath__ ( ( EQ | NE | LT | LE | GT | GE ) jsonpath__ )?
;


Expand Down
4 changes: 3 additions & 1 deletion jsonpath2/parser/JSONPath.interp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ WS

rule names:
jsonpath
jsonpath_
jsonpath__
subscript
subscriptables
subscriptableBareword
Expand All @@ -83,4 +85,4 @@ value


atn:
[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 169, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 3, 2, 3, 2, 5, 2, 35, 10, 2, 3, 2, 3, 2, 3, 3, 3, 3, 3, 3, 5, 3, 42, 10, 3, 3, 3, 5, 3, 45, 10, 3, 3, 3, 3, 3, 3, 3, 5, 3, 50, 10, 3, 3, 3, 3, 3, 5, 3, 54, 10, 3, 5, 3, 56, 10, 3, 3, 4, 3, 4, 3, 4, 3, 4, 7, 4, 62, 10, 4, 12, 4, 14, 4, 65, 11, 4, 3, 4, 3, 4, 3, 5, 3, 5, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 75, 10, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 3, 6, 5, 6, 84, 10, 6, 3, 7, 3, 7, 3, 7, 5, 7, 89, 10, 7, 3, 7, 3, 7, 3, 7, 5, 7, 94, 10, 7, 3, 8, 3, 8, 3, 9, 3, 9, 3, 9, 5, 9, 101, 10, 9, 3, 10, 3, 10, 3, 10, 5, 10, 106, 10, 10, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 3, 11, 5, 11, 116, 10, 11, 3, 11, 3, 11, 5, 11, 120, 10, 11, 5, 11, 122, 10, 11, 3, 12, 3, 12, 3, 13, 3, 13, 3, 13, 3, 13, 7, 13, 130, 10, 13, 12, 13, 14, 13, 133, 11, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 139, 10, 13, 3, 14, 3, 14, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 7, 15, 149, 10, 15, 12, 15, 14, 15, 152, 11, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 158, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 3, 16, 5, 16, 167, 10, 16, 3, 16, 2, 2, 17, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 2, 5, 4, 2, 7, 7, 29, 29, 4, 2, 3, 3, 5, 5, 3, 2, 9, 14, 2, 184, 2, 32, 3, 2, 2, 2, 4, 55, 3, 2, 2, 2, 6, 57, 3, 2, 2, 2, 8, 68, 3, 2, 2, 2, 10, 83, 3, 2, 2, 2, 12, 85, 3, 2, 2, 2, 14, 95, 3, 2, 2, 2, 16, 97, 3, 2, 2, 2, 18, 102, 3, 2, 2, 2, 20, 121, 3, 2, 2, 2, 22, 123, 3, 2, 2, 2, 24, 138, 3, 2, 2, 2, 26, 140, 3, 2, 2, 2, 28, 157, 3, 2, 2, 2, 30, 166, 3, 2, 2, 2, 32, 34, 7, 5, 2, 2, 33, 35, 5, 4, 3, 2, 34, 33, 3, 2, 2, 2, 34, 35, 3, 2, 2, 2, 35, 36, 3, 2, 2, 2, 36, 37, 7, 2, 2, 3, 37, 3, 3, 2, 2, 2, 38, 41, 7, 4, 2, 2, 39, 42, 5, 8, 5, 2, 40, 42, 5, 6, 4, 2, 41, 39, 3, 2, 2, 2, 41, 40, 3, 2, 2, 2, 42, 44, 3, 2, 2, 2, 43, 45, 5, 4, 3, 2, 44, 43, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 56, 3, 2, 2, 2, 46, 47, 7, 6, 2, 2, 47, 49, 5, 8, 5, 2, 48, 50, 5, 4, 3, 2, 49, 48, 3, 2, 2, 2, 49, 50, 3, 2, 2, 2, 50, 56, 3, 2, 2, 2, 51, 53, 5, 6, 4, 2, 52, 54, 5, 4, 3, 2, 53, 52, 3, 2, 2, 2, 53, 54, 3, 2, 2, 2, 54, 56, 3, 2, 2, 2, 55, 38, 3, 2, 2, 2, 55, 46, 3, 2, 2, 2, 55, 51, 3, 2, 2, 2, 56, 5, 3, 2, 2, 2, 57, 58, 7, 22, 2, 2, 58, 63, 5, 10, 6, 2, 59, 60, 7, 25, 2, 2, 60, 62, 5, 10, 6, 2, 61, 59, 3, 2, 2, 2, 62, 65, 3, 2, 2, 2, 63, 61, 3, 2, 2, 2, 63, 64, 3, 2, 2, 2, 64, 66, 3, 2, 2, 2, 65, 63, 3, 2, 2, 2, 66, 67, 7, 23, 2, 2, 67, 7, 3, 2, 2, 2, 68, 69, 9, 2, 2, 2, 69, 9, 3, 2, 2, 2, 70, 84, 7, 30, 2, 2, 71, 72, 7, 31, 2, 2, 72, 74, 6, 6, 2, 2, 73, 75, 5, 12, 7, 2, 74, 73, 3, 2, 2, 2, 74, 75, 3, 2, 2, 2, 75, 84, 3, 2, 2, 2, 76, 84, 5, 12, 7, 2, 77, 84, 7, 7, 2, 2, 78, 79, 7, 28, 2, 2, 79, 80, 7, 26, 2, 2, 80, 81, 5, 14, 8, 2, 81, 82, 7, 27, 2, 2, 82, 84, 3, 2, 2, 2, 83, 70, 3, 2, 2, 2, 83, 71, 3, 2, 2, 2, 83, 76, 3, 2, 2, 2, 83, 77, 3, 2, 2, 2, 83, 78, 3, 2, 2, 2, 84, 11, 3, 2, 2, 2, 85, 88, 7, 24, 2, 2, 86, 87, 7, 31, 2, 2, 87, 89, 6, 7, 3, 2, 88, 86, 3, 2, 2, 2, 88, 89, 3, 2, 2, 2, 89, 93, 3, 2, 2, 2, 90, 91, 7, 24, 2, 2, 91, 92, 7, 31, 2, 2, 92, 94, 6, 7, 4, 2, 93, 90, 3, 2, 2, 2, 93, 94, 3, 2, 2, 2, 94, 13, 3, 2, 2, 2, 95, 96, 5, 16, 9, 2, 96, 15, 3, 2, 2, 2, 97, 100, 5, 18, 10, 2, 98, 99, 7, 8, 2, 2, 99, 101, 5, 16, 9, 2, 100, 98, 3, 2, 2, 2, 100, 101, 3, 2, 2, 2, 101, 17, 3, 2, 2, 2, 102, 105, 5, 20, 11, 2, 103, 104, 7, 16, 2, 2, 104, 106, 5, 18, 10, 2, 105, 103, 3, 2, 2, 2, 105, 106, 3, 2, 2, 2, 106, 19, 3, 2, 2, 2, 107, 108, 7, 15, 2, 2, 108, 122, 5, 20, 11, 2, 109, 110, 7, 26, 2, 2, 110, 111, 5, 14, 8, 2, 111, 112, 7, 27, 2, 2, 112, 122, 3, 2, 2, 2, 113, 115, 9, 3, 2, 2, 114, 116, 5, 4, 3, 2, 115, 114, 3, 2, 2, 2, 115, 116, 3, 2, 2, 2, 116, 119, 3, 2, 2, 2, 117, 118, 9, 4, 2, 2, 118, 120, 5, 30, 16, 2, 119, 117, 3, 2, 2, 2, 119, 120, 3, 2, 2, 2, 120, 122, 3, 2, 2, 2, 121, 107, 3, 2, 2, 2, 121, 109, 3, 2, 2, 2, 121, 113, 3, 2, 2, 2, 122, 21, 3, 2, 2, 2, 123, 124, 5, 30, 16, 2, 124, 23, 3, 2, 2, 2, 125, 126, 7, 20, 2, 2, 126, 131, 5, 26, 14, 2, 127, 128, 7, 25, 2, 2, 128, 130, 5, 26, 14, 2, 129, 127, 3, 2, 2, 2, 130, 133, 3, 2, 2, 2, 131, 129, 3, 2, 2, 2, 131, 132, 3, 2, 2, 2, 132, 134, 3, 2, 2, 2, 133, 131, 3, 2, 2, 2, 134, 135, 7, 21, 2, 2, 135, 139, 3, 2, 2, 2, 136, 137, 7, 20, 2, 2, 137, 139, 7, 21, 2, 2, 138, 125, 3, 2, 2, 2, 138, 136, 3, 2, 2, 2, 139, 25, 3, 2, 2, 2, 140, 141, 7, 30, 2, 2, 141, 142, 7, 24, 2, 2, 142, 143, 5, 30, 16, 2, 143, 27, 3, 2, 2, 2, 144, 145, 7, 22, 2, 2, 145, 150, 5, 30, 16, 2, 146, 147, 7, 25, 2, 2, 147, 149, 5, 30, 16, 2, 148, 146, 3, 2, 2, 2, 149, 152, 3, 2, 2, 2, 150, 148, 3, 2, 2, 2, 150, 151, 3, 2, 2, 2, 151, 153, 3, 2, 2, 2, 152, 150, 3, 2, 2, 2, 153, 154, 7, 23, 2, 2, 154, 158, 3, 2, 2, 2, 155, 156, 7, 22, 2, 2, 156, 158, 7, 23, 2, 2, 157, 144, 3, 2, 2, 2, 157, 155, 3, 2, 2, 2, 158, 29, 3, 2, 2, 2, 159, 167, 7, 30, 2, 2, 160, 167, 7, 31, 2, 2, 161, 167, 5, 24, 13, 2, 162, 167, 5, 28, 15, 2, 163, 167, 7, 17, 2, 2, 164, 167, 7, 18, 2, 2, 165, 167, 7, 19, 2, 2, 166, 159, 3, 2, 2, 2, 166, 160, 3, 2, 2, 2, 166, 161, 3, 2, 2, 2, 166, 162, 3, 2, 2, 2, 166, 163, 3, 2, 2, 2, 166, 164, 3, 2, 2, 2, 166, 165, 3, 2, 2, 2, 167, 31, 3, 2, 2, 2, 23, 34, 41, 44, 49, 53, 55, 63, 74, 83, 88, 93, 100, 105, 115, 119, 121, 131, 138, 150, 157, 166]
[3, 24715, 42794, 33075, 47597, 16764, 15335, 30598, 22884, 3, 32, 179, 4, 2, 9, 2, 4, 3, 9, 3, 4, 4, 9, 4, 4, 5, 9, 5, 4, 6, 9, 6, 4, 7, 9, 7, 4, 8, 9, 8, 4, 9, 9, 9, 4, 10, 9, 10, 4, 11, 9, 11, 4, 12, 9, 12, 4, 13, 9, 13, 4, 14, 9, 14, 4, 15, 9, 15, 4, 16, 9, 16, 4, 17, 9, 17, 4, 18, 9, 18, 3, 2, 3, 2, 5, 2, 39, 10, 2, 3, 2, 3, 2, 3, 3, 3, 3, 5, 3, 45, 10, 3, 3, 4, 3, 4, 5, 4, 49, 10, 4, 3, 5, 3, 5, 3, 5, 5, 5, 54, 10, 5, 3, 5, 5, 5, 57, 10, 5, 3, 5, 3, 5, 3, 5, 5, 5, 62, 10, 5, 3, 5, 3, 5, 5, 5, 66, 10, 5, 5, 5, 68, 10, 5, 3, 6, 3, 6, 3, 6, 3, 6, 7, 6, 74, 10, 6, 12, 6, 14, 6, 77, 11, 6, 3, 6, 3, 6, 3, 7, 3, 7, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 87, 10, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 3, 8, 5, 8, 97, 10, 8, 3, 9, 3, 9, 3, 9, 5, 9, 102, 10, 9, 3, 9, 3, 9, 3, 9, 5, 9, 107, 10, 9, 3, 10, 3, 10, 3, 11, 3, 11, 3, 11, 5, 11, 114, 10, 11, 3, 12, 3, 12, 3, 12, 5, 12, 119, 10, 12, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 3, 13, 5, 13, 130, 10, 13, 5, 13, 132, 10, 13, 3, 14, 3, 14, 3, 15, 3, 15, 3, 15, 3, 15, 7, 15, 140, 10, 15, 12, 15, 14, 15, 143, 11, 15, 3, 15, 3, 15, 3, 15, 3, 15, 5, 15, 149, 10, 15, 3, 16, 3, 16, 3, 16, 3, 16, 3, 17, 3, 17, 3, 17, 3, 17, 7, 17, 159, 10, 17, 12, 17, 14, 17, 162, 11, 17, 3, 17, 3, 17, 3, 17, 3, 17, 5, 17, 168, 10, 17, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 3, 18, 5, 18, 177, 10, 18, 3, 18, 2, 2, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 2, 5, 4, 2, 3, 3, 5, 5, 4, 2, 7, 7, 29, 29, 3, 2, 9, 14, 2, 194, 2, 36, 3, 2, 2, 2, 4, 42, 3, 2, 2, 2, 6, 48, 3, 2, 2, 2, 8, 67, 3, 2, 2, 2, 10, 69, 3, 2, 2, 2, 12, 80, 3, 2, 2, 2, 14, 96, 3, 2, 2, 2, 16, 98, 3, 2, 2, 2, 18, 108, 3, 2, 2, 2, 20, 110, 3, 2, 2, 2, 22, 115, 3, 2, 2, 2, 24, 131, 3, 2, 2, 2, 26, 133, 3, 2, 2, 2, 28, 148, 3, 2, 2, 2, 30, 150, 3, 2, 2, 2, 32, 167, 3, 2, 2, 2, 34, 176, 3, 2, 2, 2, 36, 38, 7, 5, 2, 2, 37, 39, 5, 8, 5, 2, 38, 37, 3, 2, 2, 2, 38, 39, 3, 2, 2, 2, 39, 40, 3, 2, 2, 2, 40, 41, 7, 2, 2, 3, 41, 3, 3, 2, 2, 2, 42, 44, 9, 2, 2, 2, 43, 45, 5, 8, 5, 2, 44, 43, 3, 2, 2, 2, 44, 45, 3, 2, 2, 2, 45, 5, 3, 2, 2, 2, 46, 49, 5, 4, 3, 2, 47, 49, 5, 34, 18, 2, 48, 46, 3, 2, 2, 2, 48, 47, 3, 2, 2, 2, 49, 7, 3, 2, 2, 2, 50, 53, 7, 4, 2, 2, 51, 54, 5, 12, 7, 2, 52, 54, 5, 10, 6, 2, 53, 51, 3, 2, 2, 2, 53, 52, 3, 2, 2, 2, 54, 56, 3, 2, 2, 2, 55, 57, 5, 8, 5, 2, 56, 55, 3, 2, 2, 2, 56, 57, 3, 2, 2, 2, 57, 68, 3, 2, 2, 2, 58, 59, 7, 6, 2, 2, 59, 61, 5, 12, 7, 2, 60, 62, 5, 8, 5, 2, 61, 60, 3, 2, 2, 2, 61, 62, 3, 2, 2, 2, 62, 68, 3, 2, 2, 2, 63, 65, 5, 10, 6, 2, 64, 66, 5, 8, 5, 2, 65, 64, 3, 2, 2, 2, 65, 66, 3, 2, 2, 2, 66, 68, 3, 2, 2, 2, 67, 50, 3, 2, 2, 2, 67, 58, 3, 2, 2, 2, 67, 63, 3, 2, 2, 2, 68, 9, 3, 2, 2, 2, 69, 70, 7, 22, 2, 2, 70, 75, 5, 14, 8, 2, 71, 72, 7, 25, 2, 2, 72, 74, 5, 14, 8, 2, 73, 71, 3, 2, 2, 2, 74, 77, 3, 2, 2, 2, 75, 73, 3, 2, 2, 2, 75, 76, 3, 2, 2, 2, 76, 78, 3, 2, 2, 2, 77, 75, 3, 2, 2, 2, 78, 79, 7, 23, 2, 2, 79, 11, 3, 2, 2, 2, 80, 81, 9, 3, 2, 2, 81, 13, 3, 2, 2, 2, 82, 97, 7, 30, 2, 2, 83, 84, 7, 31, 2, 2, 84, 86, 6, 8, 2, 2, 85, 87, 5, 16, 9, 2, 86, 85, 3, 2, 2, 2, 86, 87, 3, 2, 2, 2, 87, 97, 3, 2, 2, 2, 88, 97, 5, 16, 9, 2, 89, 97, 7, 7, 2, 2, 90, 91, 7, 28, 2, 2, 91, 92, 7, 26, 2, 2, 92, 93, 5, 18, 10, 2, 93, 94, 7, 27, 2, 2, 94, 97, 3, 2, 2, 2, 95, 97, 5, 4, 3, 2, 96, 82, 3, 2, 2, 2, 96, 83, 3, 2, 2, 2, 96, 88, 3, 2, 2, 2, 96, 89, 3, 2, 2, 2, 96, 90, 3, 2, 2, 2, 96, 95, 3, 2, 2, 2, 97, 15, 3, 2, 2, 2, 98, 101, 7, 24, 2, 2, 99, 100, 7, 31, 2, 2, 100, 102, 6, 9, 3, 2, 101, 99, 3, 2, 2, 2, 101, 102, 3, 2, 2, 2, 102, 106, 3, 2, 2, 2, 103, 104, 7, 24, 2, 2, 104, 105, 7, 31, 2, 2, 105, 107, 6, 9, 4, 2, 106, 103, 3, 2, 2, 2, 106, 107, 3, 2, 2, 2, 107, 17, 3, 2, 2, 2, 108, 109, 5, 20, 11, 2, 109, 19, 3, 2, 2, 2, 110, 113, 5, 22, 12, 2, 111, 112, 7, 8, 2, 2, 112, 114, 5, 20, 11, 2, 113, 111, 3, 2, 2, 2, 113, 114, 3, 2, 2, 2, 114, 21, 3, 2, 2, 2, 115, 118, 5, 24, 13, 2, 116, 117, 7, 16, 2, 2, 117, 119, 5, 22, 12, 2, 118, 116, 3, 2, 2, 2, 118, 119, 3, 2, 2, 2, 119, 23, 3, 2, 2, 2, 120, 121, 7, 15, 2, 2, 121, 132, 5, 24, 13, 2, 122, 123, 7, 26, 2, 2, 123, 124, 5, 18, 10, 2, 124, 125, 7, 27, 2, 2, 125, 132, 3, 2, 2, 2, 126, 129, 5, 6, 4, 2, 127, 128, 9, 4, 2, 2, 128, 130, 5, 6, 4, 2, 129, 127, 3, 2, 2, 2, 129, 130, 3, 2, 2, 2, 130, 132, 3, 2, 2, 2, 131, 120, 3, 2, 2, 2, 131, 122, 3, 2, 2, 2, 131, 126, 3, 2, 2, 2, 132, 25, 3, 2, 2, 2, 133, 134, 5, 34, 18, 2, 134, 27, 3, 2, 2, 2, 135, 136, 7, 20, 2, 2, 136, 141, 5, 30, 16, 2, 137, 138, 7, 25, 2, 2, 138, 140, 5, 30, 16, 2, 139, 137, 3, 2, 2, 2, 140, 143, 3, 2, 2, 2, 141, 139, 3, 2, 2, 2, 141, 142, 3, 2, 2, 2, 142, 144, 3, 2, 2, 2, 143, 141, 3, 2, 2, 2, 144, 145, 7, 21, 2, 2, 145, 149, 3, 2, 2, 2, 146, 147, 7, 20, 2, 2, 147, 149, 7, 21, 2, 2, 148, 135, 3, 2, 2, 2, 148, 146, 3, 2, 2, 2, 149, 29, 3, 2, 2, 2, 150, 151, 7, 30, 2, 2, 151, 152, 7, 24, 2, 2, 152, 153, 5, 34, 18, 2, 153, 31, 3, 2, 2, 2, 154, 155, 7, 22, 2, 2, 155, 160, 5, 34, 18, 2, 156, 157, 7, 25, 2, 2, 157, 159, 5, 34, 18, 2, 158, 156, 3, 2, 2, 2, 159, 162, 3, 2, 2, 2, 160, 158, 3, 2, 2, 2, 160, 161, 3, 2, 2, 2, 161, 163, 3, 2, 2, 2, 162, 160, 3, 2, 2, 2, 163, 164, 7, 23, 2, 2, 164, 168, 3, 2, 2, 2, 165, 166, 7, 22, 2, 2, 166, 168, 7, 23, 2, 2, 167, 154, 3, 2, 2, 2, 167, 165, 3, 2, 2, 2, 168, 33, 3, 2, 2, 2, 169, 177, 7, 30, 2, 2, 170, 177, 7, 31, 2, 2, 171, 177, 5, 28, 15, 2, 172, 177, 5, 32, 17, 2, 173, 177, 7, 17, 2, 2, 174, 177, 7, 18, 2, 2, 175, 177, 7, 19, 2, 2, 176, 169, 3, 2, 2, 2, 176, 170, 3, 2, 2, 2, 176, 171, 3, 2, 2, 2, 176, 172, 3, 2, 2, 2, 176, 173, 3, 2, 2, 2, 176, 174, 3, 2, 2, 2, 176, 175, 3, 2, 2, 2, 177, 35, 3, 2, 2, 2, 24, 38, 44, 48, 53, 56, 61, 65, 67, 75, 86, 96, 101, 106, 113, 118, 129, 131, 141, 148, 160, 167, 176]
Loading