aristotlelib 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,15 @@
1
+ Copyright (c) 2025 Concordance Inc. dba Harmonic. All rights reserved.
2
+
3
+ Concordance Inc. dba Harmonic grants you a limited, non-exclusive, non-transferable,
4
+ revocable license to install and use this software solely to access the Aristotle
5
+ API under a valid API key and in accordance with the Aristotle API Terms of Service
6
+ available at https://aristotle.harmonic.fun/api-terms-of-use
7
+
8
+ You may not modify, distribute, sublicense, or reverse-engineer this software,
9
+ except as expressly permitted by Concordance Inc. dba Harmonic in writing.
10
+
11
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
14
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
15
+ CONTRACT, TORT OR OTHERWISE, ARISING FROM OR RELATING TO THE SOFTWARE OR ITS USE.
@@ -0,0 +1,259 @@
1
+ Metadata-Version: 2.4
2
+ Name: aristotlelib
3
+ Version: 0.1.0
4
+ Summary: Aristotle SDK - Python library for automated theorem proving with Lean
5
+ Author: Harmonic
6
+ Maintainer: Harmonic
7
+ Project-URL: Homepage, https://aristotle.harmonic.fun
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pydantic>=2.0.0
12
+ Requires-Dist: httpx>=0.24.0
13
+ Dynamic: license-file
14
+
15
+ # Aristotle SDK
16
+
17
+ The Aristotle SDK is a Python library that provides tools and utilities for interacting with the Aristotle API, enabling automated theorem proving for Lean projects.
18
+
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install aristotlelib
24
+ ```
25
+
26
+ ## Quick Start
27
+
28
+ ### 1. Set up your API key
29
+
30
+ ```python
31
+ import aristotlelib
32
+
33
+ # Set your API key
34
+ aristotlelib.set_api_key("your-api-key-here")
35
+
36
+ # Or set it via environment variable
37
+ # export ARISTOTLE_API_KEY="your-api-key-here"
38
+ ```
39
+
40
+ ### 2. Prove a theorem from a file
41
+
42
+ The simplest way to use Aristotle is to prove a theorem from a Lean file:
43
+
44
+ ```python
45
+ import asyncio
46
+
47
+ async def main():
48
+ # Prove a theorem from a Lean file
49
+ solution_path = await aristotlelib.Project.prove_from_file("path/to/your/theorem.lean")
50
+ print(f"Solution saved to: {solution_path}")
51
+
52
+ asyncio.run(main())
53
+ ```
54
+
55
+ ### 3. Manual project management
56
+
57
+ For more control, you can manage projects manually:
58
+
59
+ ```python
60
+ import asyncio
61
+ import aristotlelib
62
+ from pathlib import Path
63
+
64
+ async def main():
65
+ # Create a new project
66
+ project = await aristotlelib.Project.create()
67
+ print(f"Created project: {project.project_id}")
68
+
69
+ # Add context files
70
+ await project.add_context(["path/to/context1.lean", "path/to/context2.lean"])
71
+
72
+ # Solve with input content
73
+ await project.solve(input_content="theorem my_theorem : True := trivial")
74
+
75
+ # Wait for completion and get solution
76
+ while project.status not in [aristotlelib.ProjectStatus.COMPLETE, aristotlelib.ProjectStatus.FAILED]:
77
+ await asyncio.sleep(30) # Poll every 30 seconds
78
+ await project.refresh()
79
+ print(f"Status: {project.status}")
80
+
81
+ if project.status == aristotlelib.ProjectStatus.COMPLETE:
82
+ solution_path = await project.get_solution()
83
+ print(f"Solution saved to: {solution_path}")
84
+
85
+ asyncio.run(main())
86
+ ```
87
+
88
+ ## API Reference
89
+
90
+ ### Project Class
91
+
92
+ The main class for interacting with Aristotle projects.
93
+
94
+ #### `Project.create(context_file_paths=None, validate_lean_project_root=True)`
95
+
96
+ Create a new Aristotle project.
97
+
98
+ **Parameters:**
99
+ - `context_file_paths` (list[Path | str], optional): List of file paths to include as context
100
+ - `validate_lean_project_root` (bool): Whether to validate Lean project structure (recommended: True)
101
+
102
+ **Returns:** `Project` instance
103
+
104
+ #### `Project.prove_from_file(input_file_path, auto_add_imports=True, context_file_paths=None, validate_lean_project=True, polling_interval_seconds=30, max_polling_failures=3)`
105
+
106
+ Convenience method to prove a theorem from a file with automatic import resolution.
107
+
108
+ **Parameters:**
109
+ - `input_file_path` (Path | str): Path to the input Lean file
110
+ - `auto_add_imports` (bool): Automatically add imported files as context
111
+ - `context_file_paths` (list[Path | str], optional): Manual context files
112
+ - `validate_lean_project` (bool): Validate Lean project structure
113
+ - `polling_interval_seconds` (int): Seconds between status checks
114
+ - `max_polling_failures` (int): Max polling failures before giving up
115
+
116
+ **Returns:** `Path` to the solution file
117
+
118
+ #### `project.add_context(context_file_paths, batch_size=10, validate_lean_project_root=True)`
119
+
120
+ Add context files to an existing project.
121
+
122
+ **Parameters:**
123
+ - `context_file_paths` (list[Path | str]): Files to add as context
124
+ - `batch_size` (int): Files to upload per batch (max 10)
125
+ - `validate_lean_project_root` (bool): Validate project structure
126
+
127
+ #### `project.solve(input_file_path=None, input_content=None)`
128
+
129
+ Solve the project with either a file or text content.
130
+
131
+ **Parameters:**
132
+ - `input_file_path` (Path | str, optional): Path to input file
133
+ - `input_content` (str, optional): Text content to solve
134
+
135
+ **Note:** Exactly one of `input_file_path` or `input_content` must be provided.
136
+
137
+ #### `project.get_solution(output_path=None)`
138
+
139
+ Download the solution file.
140
+
141
+ **Parameters:**
142
+ - `output_path` (Path | str, optional): Where to save the solution
143
+
144
+ **Returns:** `Path` to the downloaded solution file
145
+
146
+ #### `project.refresh()`
147
+
148
+ Refresh the project status from the API.
149
+
150
+ ### Project Status
151
+
152
+ ```python
153
+ class ProjectStatus(Enum):
154
+ NOT_STARTED = "NOT_STARTED"
155
+ QUEUED = "QUEUED"
156
+ IN_PROGRESS = "IN_PROGRESS"
157
+ COMPLETE = "COMPLETE"
158
+ FAILED = "FAILED"
159
+ ```
160
+
161
+ ### Error Handling
162
+
163
+ The SDK provides several exception types:
164
+
165
+ - `AristotleAPIError`: API-related errors
166
+ - `LeanProjectError`: Lean project validation errors
167
+
168
+ ## Lean Project Requirements
169
+
170
+ Aristotle works best with properly structured Lean projects. Your project should have:
171
+
172
+ - A `lakefile.lean` (Lake build system) or `leanpkg.toml` (legacy)
173
+ - A `lean-toolchain` file
174
+ - Proper import structure
175
+
176
+ The SDK will automatically:
177
+ - Detect your project root
178
+ - Validate file paths are within the project
179
+ - Resolve imports to include dependencies
180
+ - Handle file size limits (100MB max per file)
181
+
182
+ ## Examples
183
+
184
+ ### Basic theorem proving
185
+
186
+ ```python
187
+ import asyncio
188
+ import aristotle
189
+
190
+ async def prove_simple_theorem():
191
+ # Set API key
192
+ aristotlelib.set_api_key("your-key")
193
+
194
+ # Prove a simple theorem
195
+ solution = await aristotlelib.Project.prove_from_file("examples/simple.lean")
196
+ print(f"Proof completed: {solution}")
197
+
198
+ asyncio.run(prove_simple_theorem())
199
+ ```
200
+
201
+ ### Working with existing projects
202
+
203
+ ```python
204
+ import asyncio
205
+ import aristotle
206
+
207
+ async def work_with_existing_project():
208
+ # Load an existing project
209
+ project = await aristotlelib.Project.from_id("existing-project-id")
210
+
211
+ # Check status
212
+ print(f"Project status: {project.status}")
213
+
214
+ if project.status == aristotlelib.ProjectStatus.COMPLETE:
215
+ solution = await project.get_solution()
216
+ print(f"Solution available at: {solution}")
217
+
218
+ asyncio.run(work_with_existing_project())
219
+ ```
220
+
221
+ ### Listing projects
222
+
223
+ ```python
224
+ import asyncio
225
+ import aristotle
226
+
227
+ async def list_projects():
228
+ projects, pagination_key = await aristotlelib.Project.list_projects(limit=10)
229
+
230
+ for project in projects:
231
+ print(f"Project {project.project_id}: {project.status}")
232
+
233
+ # Get next page if available
234
+ if pagination_key:
235
+ more_projects, pagination_key = await aristotlelib.Project.list_projects(pagination_key=pagination_key)
236
+ print(f"Found {len(more_projects)} more projects")
237
+
238
+ asyncio.run(list_projects())
239
+ ```
240
+
241
+ ## Logging
242
+
243
+ The SDK uses Python's standard logging module. To see debug and info messages from the SDK, configure logging in your application:
244
+
245
+ ```python
246
+ import logging
247
+ import aristotle
248
+
249
+ # Configure logging to see SDK messages
250
+ logging.basicConfig(
251
+ level=logging.INFO,
252
+ format="%(levelname)s - %(name)s - %(message)s"
253
+ )
254
+
255
+ # Or configure just the aristotle logger
256
+ logging.getLogger('aristotle').setLevel(logging.INFO)
257
+ ```
258
+
259
+ This will show helpful messages to track the current status.
@@ -0,0 +1,245 @@
1
+ # Aristotle SDK
2
+
3
+ The Aristotle SDK is a Python library that provides tools and utilities for interacting with the Aristotle API, enabling automated theorem proving for Lean projects.
4
+
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ pip install aristotlelib
10
+ ```
11
+
12
+ ## Quick Start
13
+
14
+ ### 1. Set up your API key
15
+
16
+ ```python
17
+ import aristotlelib
18
+
19
+ # Set your API key
20
+ aristotlelib.set_api_key("your-api-key-here")
21
+
22
+ # Or set it via environment variable
23
+ # export ARISTOTLE_API_KEY="your-api-key-here"
24
+ ```
25
+
26
+ ### 2. Prove a theorem from a file
27
+
28
+ The simplest way to use Aristotle is to prove a theorem from a Lean file:
29
+
30
+ ```python
31
+ import asyncio
32
+
33
+ async def main():
34
+ # Prove a theorem from a Lean file
35
+ solution_path = await aristotlelib.Project.prove_from_file("path/to/your/theorem.lean")
36
+ print(f"Solution saved to: {solution_path}")
37
+
38
+ asyncio.run(main())
39
+ ```
40
+
41
+ ### 3. Manual project management
42
+
43
+ For more control, you can manage projects manually:
44
+
45
+ ```python
46
+ import asyncio
47
+ import aristotlelib
48
+ from pathlib import Path
49
+
50
+ async def main():
51
+ # Create a new project
52
+ project = await aristotlelib.Project.create()
53
+ print(f"Created project: {project.project_id}")
54
+
55
+ # Add context files
56
+ await project.add_context(["path/to/context1.lean", "path/to/context2.lean"])
57
+
58
+ # Solve with input content
59
+ await project.solve(input_content="theorem my_theorem : True := trivial")
60
+
61
+ # Wait for completion and get solution
62
+ while project.status not in [aristotlelib.ProjectStatus.COMPLETE, aristotlelib.ProjectStatus.FAILED]:
63
+ await asyncio.sleep(30) # Poll every 30 seconds
64
+ await project.refresh()
65
+ print(f"Status: {project.status}")
66
+
67
+ if project.status == aristotlelib.ProjectStatus.COMPLETE:
68
+ solution_path = await project.get_solution()
69
+ print(f"Solution saved to: {solution_path}")
70
+
71
+ asyncio.run(main())
72
+ ```
73
+
74
+ ## API Reference
75
+
76
+ ### Project Class
77
+
78
+ The main class for interacting with Aristotle projects.
79
+
80
+ #### `Project.create(context_file_paths=None, validate_lean_project_root=True)`
81
+
82
+ Create a new Aristotle project.
83
+
84
+ **Parameters:**
85
+ - `context_file_paths` (list[Path | str], optional): List of file paths to include as context
86
+ - `validate_lean_project_root` (bool): Whether to validate Lean project structure (recommended: True)
87
+
88
+ **Returns:** `Project` instance
89
+
90
+ #### `Project.prove_from_file(input_file_path, auto_add_imports=True, context_file_paths=None, validate_lean_project=True, polling_interval_seconds=30, max_polling_failures=3)`
91
+
92
+ Convenience method to prove a theorem from a file with automatic import resolution.
93
+
94
+ **Parameters:**
95
+ - `input_file_path` (Path | str): Path to the input Lean file
96
+ - `auto_add_imports` (bool): Automatically add imported files as context
97
+ - `context_file_paths` (list[Path | str], optional): Manual context files
98
+ - `validate_lean_project` (bool): Validate Lean project structure
99
+ - `polling_interval_seconds` (int): Seconds between status checks
100
+ - `max_polling_failures` (int): Max polling failures before giving up
101
+
102
+ **Returns:** `Path` to the solution file
103
+
104
+ #### `project.add_context(context_file_paths, batch_size=10, validate_lean_project_root=True)`
105
+
106
+ Add context files to an existing project.
107
+
108
+ **Parameters:**
109
+ - `context_file_paths` (list[Path | str]): Files to add as context
110
+ - `batch_size` (int): Files to upload per batch (max 10)
111
+ - `validate_lean_project_root` (bool): Validate project structure
112
+
113
+ #### `project.solve(input_file_path=None, input_content=None)`
114
+
115
+ Solve the project with either a file or text content.
116
+
117
+ **Parameters:**
118
+ - `input_file_path` (Path | str, optional): Path to input file
119
+ - `input_content` (str, optional): Text content to solve
120
+
121
+ **Note:** Exactly one of `input_file_path` or `input_content` must be provided.
122
+
123
+ #### `project.get_solution(output_path=None)`
124
+
125
+ Download the solution file.
126
+
127
+ **Parameters:**
128
+ - `output_path` (Path | str, optional): Where to save the solution
129
+
130
+ **Returns:** `Path` to the downloaded solution file
131
+
132
+ #### `project.refresh()`
133
+
134
+ Refresh the project status from the API.
135
+
136
+ ### Project Status
137
+
138
+ ```python
139
+ class ProjectStatus(Enum):
140
+ NOT_STARTED = "NOT_STARTED"
141
+ QUEUED = "QUEUED"
142
+ IN_PROGRESS = "IN_PROGRESS"
143
+ COMPLETE = "COMPLETE"
144
+ FAILED = "FAILED"
145
+ ```
146
+
147
+ ### Error Handling
148
+
149
+ The SDK provides several exception types:
150
+
151
+ - `AristotleAPIError`: API-related errors
152
+ - `LeanProjectError`: Lean project validation errors
153
+
154
+ ## Lean Project Requirements
155
+
156
+ Aristotle works best with properly structured Lean projects. Your project should have:
157
+
158
+ - A `lakefile.lean` (Lake build system) or `leanpkg.toml` (legacy)
159
+ - A `lean-toolchain` file
160
+ - Proper import structure
161
+
162
+ The SDK will automatically:
163
+ - Detect your project root
164
+ - Validate file paths are within the project
165
+ - Resolve imports to include dependencies
166
+ - Handle file size limits (100MB max per file)
167
+
168
+ ## Examples
169
+
170
+ ### Basic theorem proving
171
+
172
+ ```python
173
+ import asyncio
174
+ import aristotle
175
+
176
+ async def prove_simple_theorem():
177
+ # Set API key
178
+ aristotlelib.set_api_key("your-key")
179
+
180
+ # Prove a simple theorem
181
+ solution = await aristotlelib.Project.prove_from_file("examples/simple.lean")
182
+ print(f"Proof completed: {solution}")
183
+
184
+ asyncio.run(prove_simple_theorem())
185
+ ```
186
+
187
+ ### Working with existing projects
188
+
189
+ ```python
190
+ import asyncio
191
+ import aristotle
192
+
193
+ async def work_with_existing_project():
194
+ # Load an existing project
195
+ project = await aristotlelib.Project.from_id("existing-project-id")
196
+
197
+ # Check status
198
+ print(f"Project status: {project.status}")
199
+
200
+ if project.status == aristotlelib.ProjectStatus.COMPLETE:
201
+ solution = await project.get_solution()
202
+ print(f"Solution available at: {solution}")
203
+
204
+ asyncio.run(work_with_existing_project())
205
+ ```
206
+
207
+ ### Listing projects
208
+
209
+ ```python
210
+ import asyncio
211
+ import aristotle
212
+
213
+ async def list_projects():
214
+ projects, pagination_key = await aristotlelib.Project.list_projects(limit=10)
215
+
216
+ for project in projects:
217
+ print(f"Project {project.project_id}: {project.status}")
218
+
219
+ # Get next page if available
220
+ if pagination_key:
221
+ more_projects, pagination_key = await aristotlelib.Project.list_projects(pagination_key=pagination_key)
222
+ print(f"Found {len(more_projects)} more projects")
223
+
224
+ asyncio.run(list_projects())
225
+ ```
226
+
227
+ ## Logging
228
+
229
+ The SDK uses Python's standard logging module. To see debug and info messages from the SDK, configure logging in your application:
230
+
231
+ ```python
232
+ import logging
233
+ import aristotle
234
+
235
+ # Configure logging to see SDK messages
236
+ logging.basicConfig(
237
+ level=logging.INFO,
238
+ format="%(levelname)s - %(name)s - %(message)s"
239
+ )
240
+
241
+ # Or configure just the aristotle logger
242
+ logging.getLogger('aristotle').setLevel(logging.INFO)
243
+ ```
244
+
245
+ This will show helpful messages to track the current status.
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "aristotlelib"
7
+ version = "0.1.0"
8
+ description = "Aristotle SDK - Python library for automated theorem proving with Lean"
9
+ readme = "README.md"
10
+ license-files = ["LICENSE"]
11
+ requires-python = ">=3.10"
12
+ authors = [{ name = "Harmonic" }]
13
+ maintainers = [{ name = "Harmonic" }]
14
+ dependencies = ["pydantic>=2.0.0", "httpx>=0.24.0"]
15
+
16
+ [project.urls]
17
+ Homepage = "https://aristotle.harmonic.fun"
18
+
19
+
20
+ [tool.setuptools.packages.find]
21
+ where = ["src"]
22
+
23
+ [tool.setuptools.package-dir]
24
+ "" = "src"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ from aristotlelib.api_request import set_api_key
2
+
3
+ from aristotlelib.project import Project, ProjectStatus
4
+ from aristotlelib.local_file_utils import (
5
+ find_lean_project_root,
6
+ validate_local_file_paths,
7
+ gather_file_imports,
8
+ get_files_for_upload,
9
+ )
10
+ from aristotlelib.api_request import AristotleRequestClient, AristotleAPIError
@@ -0,0 +1,129 @@
1
+ import os
2
+ import httpx
3
+ from typing import Any
4
+
5
+
6
+ API_VERSION = "1"
7
+ BASE_URL = f"https://aristotle.harmonic.fun/api/v{API_VERSION}"
8
+ DEFAULT_TIMEOUT_SECONDS = 30
9
+
10
+ API_KEY: str | None = None
11
+
12
+
13
+ def get_api_key() -> str:
14
+ global API_KEY
15
+ api_key = API_KEY or os.environ.get("ARISTOTLE_API_KEY")
16
+ if api_key is None:
17
+ raise ValueError(
18
+ "API key has not been set. Call aristotle.set_api_key() or set the ARISTOTLE_API_KEY environment variable."
19
+ )
20
+ return api_key
21
+
22
+
23
+ def set_api_key(api_key: str) -> str:
24
+ global API_KEY
25
+ API_KEY = api_key
26
+ return API_KEY
27
+
28
+
29
+ class AristotleRequestClient:
30
+ """Async HTTP client for the Aristotle API."""
31
+
32
+ def __init__(self):
33
+ self._client: httpx.AsyncClient | None = None
34
+
35
+ async def __aenter__(self):
36
+ """Async context manager entry."""
37
+ self._client = httpx.AsyncClient(timeout=DEFAULT_TIMEOUT_SECONDS)
38
+ return self
39
+
40
+ async def __aexit__(self, exc_type, exc_val, exc_tb):
41
+ """Async context manager exit."""
42
+ if self._client:
43
+ await self._client.aclose()
44
+
45
+ async def get(
46
+ self, endpoint: str, params: dict[str, Any] | None = None
47
+ ) -> httpx.Response:
48
+ """Make a GET request."""
49
+ return await self._make_request("GET", endpoint, params=params)
50
+
51
+ async def post(
52
+ self,
53
+ endpoint: str,
54
+ data: dict[str, Any] | None = None,
55
+ files: list[tuple[str, tuple[str, bytes, str]]] | None = None,
56
+ ) -> httpx.Response:
57
+ """Make a POST request."""
58
+ return await self._make_request("POST", endpoint, data=data, files=files)
59
+
60
+ async def put(
61
+ self, endpoint: str, data: dict[str, Any] | None = None
62
+ ) -> httpx.Response:
63
+ """Make a PUT request."""
64
+ return await self._make_request("PUT", endpoint, data=data)
65
+
66
+ async def delete(self, endpoint: str) -> httpx.Response:
67
+ """Make a DELETE request."""
68
+ return await self._make_request("DELETE", endpoint)
69
+
70
+ async def _make_request(
71
+ self,
72
+ method: str,
73
+ endpoint: str,
74
+ data: dict[str, Any] | None = None,
75
+ params: dict[str, Any] | None = None,
76
+ files: list[tuple[str, tuple[str, bytes, str]]] | None = None,
77
+ ) -> httpx.Response:
78
+ """Make an HTTP request to the Aristotle API."""
79
+ url = f"{BASE_URL}/{endpoint.lstrip('/')}"
80
+ headers = {
81
+ "X-API-Key": get_api_key(),
82
+ }
83
+
84
+ if not self._client:
85
+ self._client = httpx.AsyncClient(timeout=DEFAULT_TIMEOUT_SECONDS)
86
+
87
+ try:
88
+ if files:
89
+ # For file uploads, use multipart/form-data
90
+ files_data = []
91
+ for field_name, (file_path, file_content, content_type) in files:
92
+ files_data.append(
93
+ (field_name, (file_path, file_content, content_type))
94
+ )
95
+
96
+ response = await self._client.request(
97
+ method=method,
98
+ url=url,
99
+ data=data,
100
+ files=files_data,
101
+ params=params,
102
+ headers=headers,
103
+ )
104
+ else:
105
+ # For regular requests, use JSON
106
+ headers["Content-Type"] = "application/json"
107
+ response = await self._client.request(
108
+ method=method,
109
+ url=url,
110
+ json=data,
111
+ params=params,
112
+ headers=headers,
113
+ )
114
+ response.raise_for_status()
115
+ return response
116
+ except httpx.RequestError as e:
117
+ raise AristotleAPIError(f"Request failed: {str(e)}") from e
118
+
119
+ async def close(self):
120
+ """Close the async client."""
121
+ if self._client:
122
+ await self._client.aclose()
123
+ self._client = None
124
+
125
+
126
+ class AristotleAPIError(Exception):
127
+ """Exception raised for API-related errors."""
128
+
129
+ pass