import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Predicates

The three predicate methods on `OctetSdk.loc`. All three are `async` (Swift) / `suspend` (Kotlin) and **do not throw**. Runtime problems surface as `INDETERMINATE` verdicts, not exceptions.

## `isWithin`

The primary predicate. Reads as: *"my currently provable location is inside `region`."*

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
public func isWithin(
    region: OctetRegion,
    atTime: Date = Date(),
    sessionNonce: Data? = nil,
    decisionRef: String? = nil,
    forceFresh: Bool = false
) async -> OctetVerdict
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
suspend fun isWithin(
    region: OctetRegion,
    atTime: Instant = Instant.now(),
    sessionNonce: ByteArray? = null,
    decisionRef: String? = null,
    forceFresh: Boolean = false,
): OctetVerdict
```

  </TabItem>
</Tabs>

### Example

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
let v = await sdk.loc.isWithin(
    region: .country(isoCode: "US"),
    atTime: Date()
)
if v.result == .yes { ship(v.proof!) }
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
val v = sdk.loc.isWithin(
    region = OctetRegion.country("US"),
    atTime = Instant.now()
)
if (v.result == OctetVerdict.Result.YES) ship(v.proof!!)
```

  </TabItem>
</Tabs>

---

## `isOutside`

Reads as: *"my currently provable location is outside `region`."*

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
public func isOutside(
    region: OctetRegion,
    atTime: Date = Date(),
    sessionNonce: Data? = nil,
    decisionRef: String? = nil,
    forceFresh: Bool = false
) async -> OctetVerdict
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
suspend fun isOutside(
    region: OctetRegion,
    atTime: Instant = Instant.now(),
    sessionNonce: ByteArray? = null,
    decisionRef: String? = null,
    forceFresh: Boolean = false,
): OctetVerdict
```

  </TabItem>
</Tabs>

:::caution

**`isOutside` is not `!isWithin`.** An `INDETERMINATE` is not `outside`. The separate call lets the caller assert the negative claim and get a *proof of the negative*. A verifier can check that proof just as easily as a `YES`. Negating an `INDETERMINATE` would erase the difference between "I'm not inside" and "I can't tell".

:::

---

## `contains`

Reads from the device's perspective: *"my location contains this point within `tol` meters."*

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
public func contains(
    center: LatLon,
    tol: Meters,
    atTime: Date = Date(),
    sessionNonce: Data? = nil,
    decisionRef: String? = nil,
    forceFresh: Bool = false
) async -> OctetVerdict
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
suspend fun contains(
    center: LatLon,
    tol: Meters,
    atTime: Instant = Instant.now(),
    sessionNonce: ByteArray? = null,
    decisionRef: String? = null,
    forceFresh: Boolean = false,
): OctetVerdict
```

  </TabItem>
</Tabs>

Mathematically equivalent to `isWithin(region: .disc(center: center, radiusMeters: tol), atTime:)`. Kept as a distinct verb because the device-centric reading is the natural one for proximity use cases ("am I near the delivery dropoff", "did I arrive at the geofence").

### Rolling your own

`isWithin` is the load-bearing primitive. `contains` is a thin one-liner over it. The same trick rolls custom predicates:

<Tabs groupId="platform">
  <TabItem value="swift" label="Swift (iOS)">

```swift
extension OctetLoc {
    func isNear(_ center: LatLon, _ tol: Meters) async -> OctetVerdict {
        await isWithin(region: .disc(center: center, radiusMeters: tol))
    }
    func isInsideAny(_ regions: [OctetRegion]) async -> OctetVerdict? {
        for r in regions {
            let v = await isWithin(region: r)
            if v.result == .yes { return v }
        }
        return nil
    }
}
```

  </TabItem>
  <TabItem value="kotlin" label="Kotlin (Android)">

```kotlin
suspend fun OctetLoc.isNear(center: LatLon, tol: Meters): OctetVerdict =
    isWithin(OctetRegion.disc(center, tol))

suspend fun OctetLoc.isInsideAny(regions: List<OctetRegion>): OctetVerdict? {
    for (r in regions) {
        val v = isWithin(r)
        if (v.result == OctetVerdict.Result.YES) return v
    }
    return null
}
```

  </TabItem>
</Tabs>

## Time semantics

All three predicates take an optional `atTime`. See [Time Semantics](/docs/v2.0/concepts/time-semantics/) for the live / historical / future regimes and the per-resolution validity windows. Default is "now". Future times beyond ±2 s come back `INDETERMINATE / FUTURE_TIME`.

## Proof binding and freshness

All three predicates take three optional controls over which proof answers the call:

- **`sessionNonce`** (opaque bytes, 1 to 512) binds the proof to one login. The SDK commits a hash of the nonce inside the signed proof, so a verifier can confirm the proof was made for that login. An empty or over-512-byte nonce returns `INDETERMINATE / INVALID_SESSION_NONCE`.
- **`decisionRef`** (1 to 256 characters) binds the uploaded proof to one authorization decision, tagging the upload envelope with `decision_ref` and `region_ref`. An over-256-character ref returns `INDETERMINATE / INVALID_DECISION_REF`, with no network call.
- **`forceFresh`** bypasses the freshness buffer and mints a proof for this call, with none of the binding semantics above.

Each of `sessionNonce` and `decisionRef` forces a fresh proof. Setting `forceFresh` as well is redundant but harmless.

```swift
let nonce = try await loginBackend.issueNonce()
let v = await sdk.loc.isWithin(region: .country(isoCode: "US"), sessionNonce: nonce)
if v.result == .yes { send(v.proof!, to: loginBackend) }
```

[Proof Binding](/docs/v2.0/concepts/proof-binding/) explains when to use each, and how a bound proof is verified.

## See also

- [`OctetRegion`](/docs/v2.0/api-reference/octet-region/). The shapes you can pass.
- [`OctetVerdict`](/docs/v2.0/api-reference/octet-verdict/). What you get back.
- [Proof Binding](/docs/v2.0/concepts/proof-binding/). Binding a proof to a login or a decision.
- [Verdicts](/docs/v2.0/concepts/verdicts/). The trichotomy and the `ReasonCode` taxonomy.
