Skip to content

Commit

Permalink
remove large if, add new divisor logic
Browse files Browse the repository at this point in the history
  • Loading branch information
ShawnMorreau committed Oct 8, 2024
1 parent cb28d4d commit f41f944
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 47 deletions.
15 changes: 7 additions & 8 deletions ark/fast-check/__tests__/arktypeFastCheck.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@ import { attest } from "@ark/attest"
import { arkToArbitrary } from "@ark/fast-check/internal/arktypeFastCheck.ts"
import { ArkErrors } from "@ark/schema"
import { scope, type } from "arktype"
import { cyclic10 } from "arktype/internal/__tests__/generated/cyclic.ts"
import { assert, property, type Arbitrary } from "fast-check"
import { describe, it } from "mocha"

const assertProperty = (arbitrary: Arbitrary<unknown>, schema: type.Any) =>
assert(
property(arbitrary, value => {
const result = schema(value)
console.log(value)
return !(result instanceof ArkErrors)
schema.assert(value)
return true
})
)

Expand Down Expand Up @@ -53,6 +52,11 @@ describe("Arbitrary Generation", () => {
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("large divisor", () => {
const t = type("number%7654321001>1")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("divisible within range", () => {
const t = type("15<number%7<39")
const arbitrary = arkToArbitrary(t)
Expand Down Expand Up @@ -148,11 +152,6 @@ describe("Arbitrary Generation", () => {
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("multiple aliases", () => {
const t = scope(cyclic10).type("user&user2")
const arbitrary = arkToArbitrary(t)
assertProperty(arbitrary, t)
})
it("cyclic throws", () => {
const $ = scope({
arf2: {
Expand Down
10 changes: 4 additions & 6 deletions ark/fast-check/arbitraryBuilders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,10 @@ export const arbitraryBuilders: ArbitraryBuilders = {

const handleAlias = (fastCheckContext: FastCheckContext) => {
const tieStack = fastCheckContext.fastCheckTies
if (tieStack.length) {
const tie = tieStack.at(-1)!
const id = (fastCheckContext.currentNodeContext as StructureNodeContext).id
return tie(id)
}
throwInternalError("Tie has not been initialized")
if (!(tieStack.length > 0)) throwInternalError("Tie has not been initialized")
const tie = tieStack[tieStack.length - 1]
const id = (fastCheckContext.currentNodeContext as StructureNodeContext).id
return tie(id)
}

export const getKey = (nodeContext: NodeContext): keyof ArbitraryBuilders => {
Expand Down
77 changes: 44 additions & 33 deletions ark/fast-check/terminalArbitraries.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { hasKey } from "@ark/util"
import { hasKey, throwInternalError } from "@ark/util"

Check warning on line 1 in ark/fast-check/terminalArbitraries.ts

View workflow job for this annotation

GitHub Actions / core

'throwInternalError' is defined but never used
import {
date,
double,
Expand Down Expand Up @@ -66,42 +66,53 @@ export const generateNumberArbitrary = (
const hasMax = condensedNodeContext.max !== undefined
const hasMin = condensedNodeContext.min !== undefined

if ("divisor" in condensedNodeContext) {
const constraints = getNumericBoundConstraints(
fastCheckContext,
["min", "max"],
{
min: (value: number) => Math.ceil(value),
max: (value: number) => Math.floor(value)
}
)
if (hasMin && hasMax && constraints.min > constraints.max) {
throw new Error(
`No integer value satisfies >${constraints.min} & <${constraints.max}`
)
}

const firstDivisibleInRange =
Math.ceil(
(constraints.min ?? Number.MIN_SAFE_INTEGER) /
condensedNodeContext.divisor
) * condensedNodeContext.divisor

if (firstDivisibleInRange > (constraints.max ?? Number.MAX_SAFE_INTEGER)) {
throw new Error(
`No values within range ${constraints.min} - ${constraints.max} are divisible by ${condensedNodeContext.divisor}.`
)
if (condensedNodeContext.divisor === undefined) {
const constraints = getNumericBoundConstraints(fastCheckContext, [
"min",
"max"
])
return double(constraints)
}
const divisor = condensedNodeContext.divisor
const constraints = getNumericBoundConstraints(
fastCheckContext,
["min", "max"],
{
min: (value: number) => Math.ceil(value),
max: (value: number) => Math.floor(value)
}
)
if (hasMin && hasMax && constraints.min > constraints.max) {
throw new Error(
`No integer value satisfies >${constraints.min} & <${constraints.max}`
)
}
const min = constraints.min ?? Number.MIN_SAFE_INTEGER
const max = constraints.max ?? Number.MAX_SAFE_INTEGER
const firstDivisibleInRange = Math.ceil(min / divisor) * divisor

return integer(constraints).filter(
num => num % condensedNodeContext.divisor! === 0
if (firstDivisibleInRange > max || firstDivisibleInRange < min) {
throw new Error(
`No values within range ${constraints.min} - ${constraints.max} are divisible by ${divisor}.`
)
}
const constraints = getNumericBoundConstraints(fastCheckContext, [
"min",
"max"
])
return double(constraints)
constraints.min = firstDivisibleInRange
//fast-check defaults max to 0x7fffffff which prevents larger divisible numbers from being produced
constraints.max = max
const integerArbitrary = integer(constraints)
const integersDivisibleByDivisor = integerArbitrary.map(value => {
const remainder = value % divisor
if (remainder === 0) return value

const lowerPossibleValue = value - remainder
if (
lowerPossibleValue >= firstDivisibleInRange &&
lowerPossibleValue % divisor === 0
)
return lowerPossibleValue
return value + remainder
})
return integersDivisibleByDivisor
}

const getNumericBoundConstraints: ConstraintContext = (
Expand Down

0 comments on commit f41f944

Please sign in to comment.