@mark1russell7/strategy-space 0.0.0-stage → 0.0.1

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,211 @@
1
+ // src/strategy-options.ts
2
+ /** The number of values of a sweep on a range when the call and the declaration give no number. */
3
+ const SWEEP_COUNT = 5;
4
+ /** This function gives the declarations of a strategy as a record. A strategy without a schema gives an empty record. */
5
+ function schemaOf(strategy) {
6
+ return strategy.schema ?? {};
7
+ }
8
+ /** This function gives the declared options of a strategy, in the order of its schema, with the effective default of each one. */
9
+ function listOptions(strategy) {
10
+ const defaults = optionDefaults(strategy);
11
+ return Object.entries(schemaOf(strategy)).map(([key, declaration]) => ({
12
+ key,
13
+ declaration,
14
+ value: defaults[key]
15
+ }));
16
+ }
17
+ /**
18
+ * This function gives the effective default of each declared option: the value of `defaults`, else the `default` of the declaration.
19
+ * An option without either value stays out. The strategy gives the same output with this object as with no options.
20
+ */
21
+ function optionDefaults(strategy) {
22
+ const out = {};
23
+ const defaults = strategy.defaults;
24
+ for (const [key, declaration] of Object.entries(schemaOf(strategy))) if (key in defaults) out[key] = defaults[key];
25
+ else if (declaration.default !== void 0) out[key] = declaration.default;
26
+ for (const key of Object.keys(defaults)) if (!(key in out)) out[key] = defaults[key];
27
+ return out;
28
+ }
29
+ /** This function gives `true` if the number is one of the special values of the declaration. */
30
+ const isSpecial = (d, v) => (d.special ?? []).some((s) => Object.is(s, v) || s === v);
31
+ /** This function gives the problem of one value against its declaration, or `undefined` when the value fits. */
32
+ function optionIssue(key, value, d) {
33
+ const issue = (problem, message) => ({
34
+ key,
35
+ value,
36
+ problem,
37
+ message
38
+ });
39
+ if (value === void 0) return void 0;
40
+ switch (d.type) {
41
+ case "number":
42
+ case "integer":
43
+ if (typeof value !== "number" || Number.isNaN(value)) return issue("type", `The option "${key}" takes a number.`);
44
+ if (isSpecial(d, value)) return void 0;
45
+ if (d.values !== void 0 && !d.values.includes(value)) return issue("value", `The option "${key}" takes ${d.values.join(", ")}, not ${value}.`);
46
+ if (value < d.min || value > d.max) return issue("range", `The option "${key}" is ${value}, out of the range ${d.min} to ${d.max}.`);
47
+ if (d.type === "integer" && !Number.isInteger(value)) return issue("integer", `The option "${key}" takes a whole number, not ${value}.`);
48
+ return;
49
+ case "boolean": return typeof value === "boolean" ? void 0 : issue("type", `The option "${key}" takes true or false.`);
50
+ case "enum":
51
+ if (d.custom === "function" && typeof value === "function") return void 0;
52
+ return d.values.includes(value) ? void 0 : issue("value", `The option "${key}" takes ${d.values.join(", ")}, not ${String(value)}.`);
53
+ case "function": return typeof value === "function" ? void 0 : issue("type", `The option "${key}" takes a function.`);
54
+ case "list": return Array.isArray(value) ? void 0 : issue("type", `The option "${key}" takes a list.`);
55
+ default: return typeof value === "object" && value !== null ? void 0 : issue("type", `The option "${key}" takes an object.`);
56
+ }
57
+ }
58
+ /**
59
+ * This function examines option values against the schema of a strategy. It gives one issue for each key without a declaration.
60
+ * It also gives one issue for each value of the wrong type, out of the range, not whole or not one of the values.
61
+ * It changes no value.
62
+ * A strategy without a schema gives no issue.
63
+ */
64
+ function checkOptions(strategy, options) {
65
+ if (strategy.schema === void 0) return [];
66
+ const schema = schemaOf(strategy);
67
+ const out = [];
68
+ for (const [key, value] of Object.entries(options)) {
69
+ const d = schema[key];
70
+ const issue = d ? optionIssue(key, value, d) : {
71
+ key,
72
+ value,
73
+ problem: "unknown",
74
+ message: `The strategy "${strategy.name}" has no option "${key}".`
75
+ };
76
+ if (issue) out.push(issue);
77
+ }
78
+ return out;
79
+ }
80
+ /**
81
+ * This function moves each number option into the range of its declaration, for a control. An integer option gets the nearest whole number.
82
+ * An option with a list of `values` gets the nearest value of the list. A special value stays. The function keeps each other value as it is. `checkOptions` reports the values that this function cannot correct.
83
+ */
84
+ function clampOptions(strategy, options) {
85
+ const schema = schemaOf(strategy);
86
+ const out = { ...options };
87
+ for (const [key, value] of Object.entries(options)) {
88
+ const d = schema[key];
89
+ if (!d || d.type !== "number" && d.type !== "integer" || typeof value !== "number" || Number.isNaN(value) || isSpecial(d, value)) continue;
90
+ if (d.values !== void 0 && d.values.length > 0) {
91
+ out[key] = d.values.reduce((best, v) => Math.abs(v - value) < Math.abs(best - value) ? v : best);
92
+ continue;
93
+ }
94
+ const v = Math.min(d.max, Math.max(d.min, value));
95
+ out[key] = d.type === "integer" ? Math.min(d.max, Math.max(d.min, Math.round(v))) : v;
96
+ }
97
+ return out;
98
+ }
99
+ /** This function gives `count` values from `min` to `max` on the scale, on the grain of the step, without repeats. */
100
+ function rangeValues(d, count) {
101
+ const n = Math.max(1, Math.floor(count));
102
+ const log = d.scale === "log" && d.min > 0;
103
+ const at = (t) => log ? d.min * (d.max / d.min) ** t : d.min + (d.max - d.min) * t;
104
+ const step = d.step ?? (d.type === "integer" ? 1 : void 0);
105
+ const snap = (v) => {
106
+ const s = step === void 0 ? v : d.min + Math.round((v - d.min) / step) * step;
107
+ const r = Number(s.toPrecision(12));
108
+ return Math.min(d.max, Math.max(d.min, d.type === "integer" ? Math.round(r) : r));
109
+ };
110
+ const out = [];
111
+ for (let i = 0; i < n; i++) {
112
+ const v = snap(at(n === 1 ? 0 : i / (n - 1)));
113
+ if (!out.includes(v)) out.push(v);
114
+ }
115
+ return out;
116
+ }
117
+ /**
118
+ * This function gives the values of a sweep of one option declaration.
119
+ * A number option gives its `marks`, or its list of `values`, or `count` values on its range and its scale. A boolean gives `false` and `true`. An enum gives its values.
120
+ * An option of another type has no sweep, so the function throws an error.
121
+ */
122
+ function sweepValues(declaration, options = {}) {
123
+ switch (declaration.type) {
124
+ case "number":
125
+ case "integer": {
126
+ if (options.count === void 0 && declaration.marks !== void 0) return [...declaration.marks];
127
+ if (declaration.values !== void 0) return [...declaration.values];
128
+ const values = rangeValues(declaration, options.count ?? SWEEP_COUNT);
129
+ return options.special ? [...values, ...(declaration.special ?? []).filter((s) => !values.includes(s))] : values;
130
+ }
131
+ case "boolean": return [false, true];
132
+ case "enum": return [...declaration.values];
133
+ default: throw new Error(`An option of the type "${declaration.type}" has no sweep. A sweep takes a number, an integer, a boolean or an enum.`);
134
+ }
135
+ }
136
+ /** This function gives the declaration of one option of a strategy. When the strategy has no such declaration, the function throws an error. */
137
+ function declarationOf(strategy, key) {
138
+ const d = schemaOf(strategy)[key];
139
+ if (!d) throw new Error(`The strategy "${strategy.name}" has no declaration of the option "${key}".`);
140
+ return d;
141
+ }
142
+ /**
143
+ * This function gives the settings of a sweep of one option of a strategy, from its declaration only.
144
+ * The name of each setting is the key and the value, for example `stretch 1.5`.
145
+ */
146
+ function sweepOf(strategy, key, options = {}) {
147
+ return sweepValues(declarationOf(strategy, key), options).map((value) => ({
148
+ name: `${key} ${String(value)}`,
149
+ options: { [key]: value }
150
+ }));
151
+ }
152
+ /**
153
+ * This function gives the settings of a grid of options: each combination of the values of the sweeps of the keys.
154
+ * The name of a setting joins the names of its values with commas, in the order of the keys.
155
+ */
156
+ function sweepGrid(strategy, keys, options = {}) {
157
+ let out = [{
158
+ name: "",
159
+ options: {}
160
+ }];
161
+ for (const key of keys) {
162
+ const sweep = sweepOf(strategy, key, options);
163
+ out = out.flatMap((s) => sweep.map((t) => ({
164
+ name: s.name === "" ? t.name : `${s.name}, ${t.name}`,
165
+ options: {
166
+ ...s.options,
167
+ ...t.options
168
+ }
169
+ })));
170
+ }
171
+ return keys.length === 0 ? [] : out;
172
+ }
173
+ /** This function gives the problems of one declaration: a bad range, a bad step, or marks and a default that do not fit. */
174
+ function declarationIssues(key, d) {
175
+ const out = [];
176
+ if (d.describe.trim().length === 0) out.push(`The option "${key}" has no description.`);
177
+ if (d.type === "number" || d.type === "integer") {
178
+ if (!Number.isFinite(d.min) || !Number.isFinite(d.max) || d.min > d.max) out.push(`The option "${key}" has the bad range ${d.min} to ${d.max}.`);
179
+ if (d.type === "integer" && (!Number.isInteger(d.min) || !Number.isInteger(d.max))) out.push(`The integer option "${key}" has a range that is not whole.`);
180
+ if (d.step !== void 0 && !(d.step > 0)) out.push(`The option "${key}" has the step ${d.step}. A step is more than 0.`);
181
+ if (d.scale === "log" && !(d.min > 0)) out.push(`The option "${key}" has the scale log and the min ${d.min}. The scale log needs a min above 0.`);
182
+ if (d.values !== void 0 && d.values.length === 0) out.push(`The number option "${key}" has an empty list of values.`);
183
+ for (const v of d.values ?? []) if (v < d.min || v > d.max || d.type === "integer" && !Number.isInteger(v)) out.push(`The value ${v} of the option "${key}" is not on its range ${d.min} to ${d.max}.`);
184
+ for (const v of [...d.marks ?? [], ...d.default === void 0 ? [] : [d.default]]) {
185
+ const issue = optionIssue(key, v, d);
186
+ if (issue) out.push(issue.message);
187
+ }
188
+ } else if (d.type === "enum") {
189
+ if (d.values.length === 0) out.push(`The enum option "${key}" has no values.`);
190
+ if (d.default !== void 0 && !d.values.includes(d.default)) out.push(`The default of the option "${key}" is not one of its values.`);
191
+ } else if (d.type === "boolean") {
192
+ if (d.default !== void 0 && typeof d.default !== "boolean") out.push(`The default of the option "${key}" is not true or false.`);
193
+ }
194
+ return out;
195
+ }
196
+ /**
197
+ * This function examines the schema of a strategy. It gives one message for each option of `defaults` without a declaration.
198
+ * It also gives one message for each bad declaration, and for each value of `defaults` that does not fit its declaration.
199
+ * A good schema gives an empty list.
200
+ */
201
+ function checkSchema(strategy) {
202
+ const schema = schemaOf(strategy);
203
+ const out = [];
204
+ for (const key of Object.keys(strategy.defaults)) if (!(key in schema)) out.push(`The strategy "${strategy.name}" has the option "${key}" in its defaults, without a declaration.`);
205
+ for (const [key, d] of Object.entries(schema)) out.push(...declarationIssues(key, d).map((m) => `The strategy "${strategy.name}": ${m}`));
206
+ out.push(...checkOptions(strategy, strategy.defaults).filter((i) => i.problem !== "unknown").map((i) => `The strategy "${strategy.name}": ${i.message}`));
207
+ return out;
208
+ }
209
+ export { checkOptions, checkSchema, clampOptions, declarationIssues, listOptions, optionDefaults, optionIssue, sweepGrid, sweepOf, sweepValues };
210
+
211
+ //# sourceMappingURL=strategy-options.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy-options.js","names":[],"sources":["../src/strategy-options.ts"],"sourcesContent":["import type { ParamWhen } from \"./strategy-space.ts\";\n\n/** The scale of a number option. A sweep on the scale `log` gives values with equal ratios, not with equal differences. */\nexport type OptionScale = \"linear\" | \"log\";\n\n/** The reason why an option has no default value. The strategy reads the absence, the caller sets it, the strategy calculates it, or the default is code. */\nexport type NoDefaultReason = \"unset\" | \"required\" | \"derived\" | \"function\";\n\n/** The statement of an option without a default value that JSON can hold. */\nexport interface NoDefault {\n readonly reason: NoDefaultReason;\n /** One short sentence that tells what the strategy does without a value. */\n readonly describe: string;\n}\n\n/** The fields of each option declaration. */\ninterface OptionBase {\n /** One short sentence that tells what the option sets. The site and the documents show it. */\n readonly describe: string;\n /** This field tells why the option has no default. The catalog of an engine has this field or a `default` on each option. */\n readonly noDefault?: NoDefault;\n /** The option is active only when an earlier option of the same strategy has one of these values. */\n readonly when?: ParamWhen;\n}\n\n/**\n * The declaration of a number option. The range from `min` to `max` is the range of a control and of a sweep.\n * The strategy can take a value outside the range. `checkOptions` reports such a value, and `clampOptions` moves it into the range.\n */\nexport interface NumberOption extends OptionBase {\n /** The type `integer` takes only whole numbers. */\n readonly type: \"number\" | \"integer\";\n readonly min: number;\n readonly max: number;\n /** The grain of a control and of a sweep. A value off the step is valid. Without it, the grain is free (or 1 for an integer). */\n readonly step?: number;\n /** The scale of a control and of a sweep. The default is `linear`. The scale `log` needs a `min` above 0. */\n readonly scale?: OptionScale;\n /** These values are valid outside the range. For example, `Infinity` sets no limit. */\n readonly special?: readonly number[];\n /** These values are the notable values of the option: the default and the values of the sweeps of the benchmark. */\n readonly marks?: readonly number[];\n /**\n * These values are the only valid values of the option, in the order of a control. A control shows them as a list, and a sweep gives them.\n * Without them, each value on the range is valid.\n */\n readonly values?: readonly number[];\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: number;\n}\n\n/** The declaration of a boolean option. */\nexport interface BooleanOption extends OptionBase {\n readonly type: \"boolean\";\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: boolean;\n}\n\n/** The declaration of an option with a fixed list of string values. */\nexport interface EnumOption<V extends string = string> extends OptionBase {\n readonly type: \"enum\";\n /** The values of the option, in the order of a control. */\n readonly values: readonly V[];\n /**\n * With `function`, the option also takes a rule of the caller in place of a name. A control sets only the names.\n * Each name is a known rule of the strategy.\n */\n readonly custom?: \"function\";\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: V;\n}\n\n/**\n * The declaration of an option that a control or a sweep cannot set from a range.\n * The type `function` is a rule, `strategy` is a strategy of the step `step`, `list` is an array and `object` is any other object.\n */\nexport interface ValueOption<V = unknown, S extends string = string> extends OptionBase {\n readonly type: \"function\" | \"strategy\" | \"list\" | \"object\";\n /** The step of the strategy, for the type `strategy`. A control lists the strategies of this step of a registry. */\n readonly step?: S;\n /** The kinds of strategy that the option takes, for the type `strategy`. Without them, the option takes each kind. */\n readonly kinds?: readonly string[];\n /** The key of the option that holds the options of the chosen strategy, for the type `strategy`. */\n readonly optionsKey?: string;\n /** The value `true` lets a value hold its options: a key, or an object with the fields `strategy`, `options` and `id`. This field is for the type `strategy`, and also for an item of a list. */\n readonly inlineOptions?: boolean;\n /** The value `false` leaves out the opt-in strategies, because the option finds only the default set of the engine. This field is for the type `strategy`. */\n readonly optIn?: boolean;\n /** For each dimension of `supports`, the value that a chosen strategy must take. This field is for the type `strategy`, and also for an item of a list. */\n readonly needs?: Readonly<Record<string, string>>;\n /** The declaration of each item, for the type `list`. */\n readonly of?: ListItem<S>;\n /** The smallest number of items, for the type `list`. */\n readonly min?: number;\n /** The largest number of items, for the type `list`. */\n readonly max?: number;\n /** The strategy uses this value when the caller and `defaults` give no value. */\n readonly default?: V;\n}\n\n/** The declaration of one option of any type. */\nexport type AnyOption<S extends string = string> = NumberOption | BooleanOption | EnumOption | ValueOption<unknown, S>;\n\n/** This type removes the fields of a whole option from a declaration. */\ntype ItemOf<T> = T extends unknown ? Omit<T, \"describe\" | \"default\" | \"noDefault\" | \"when\"> : never;\n\n/** The declaration of one item of a list option: an option without its text, its default and its condition. */\nexport type ListItem<S extends string = string> = ItemOf<AnyOption<S>>;\n\n/**\n * The declaration that fits the type of an option value: numbers, booleans, string unions or other values.\n * A union of strings and other values takes an enum of the strings or a value declaration. An example is a rule or the name of a rule.\n * A plain `string` takes an enum or a value declaration, because the value can be the key of a strategy.\n */\nexport type OptionDeclaration<V, S extends string = string> = [V] extends [number]\n ? NumberOption\n : [V] extends [boolean]\n ? BooleanOption\n : [V] extends [string]\n ? string extends V\n ? EnumOption<V> | ValueOption<V, S>\n : EnumOption<V>\n : [Extract<V, string>] extends [never]\n ? ValueOption<V, S>\n : EnumOption<Extract<V, string>> | ValueOption<V, S>;\n\n/** The declarations of the options of a strategy. The schema has one declaration for each key of the options type, and no other key. */\nexport type OptionSchema<O, S extends string = string> = { readonly [K in keyof O]-?: OptionDeclaration<NonNullable<O[K]>, S> };\n\n/** The fields of a strategy that the functions of this module read. An engine can add other fields. */\nexport interface DeclaredStrategy<O extends object = object, S extends string = string> {\n /** The name of the strategy in a message. */\n readonly name: string;\n readonly defaults: O;\n readonly schema?: OptionSchema<O, S>;\n}\n\n/** One problem of an option value against its declaration. */\nexport interface OptionIssue {\n /** The key of the option. */\n readonly key: string;\n readonly value: unknown;\n /** The kind of the problem: no declaration, the wrong type, out of the range, not a whole number or not one of the values. */\n readonly problem: \"unknown\" | \"type\" | \"range\" | \"integer\" | \"value\";\n /** One sentence that tells the problem. */\n readonly message: string;\n}\n\n/** One setting of a sweep: a name for the rows of a table, and the options of the strategy. */\nexport interface OptionSetting<O extends object> {\n /** The key and the value of each option of the setting, for example `stretch 1.5`. */\n readonly name: string;\n readonly options: Partial<O>;\n}\n\n/** The options of `sweepValues`, `sweepOf` and `sweepGrid`. */\nexport interface SweepOptions {\n /**\n * The number of values on the range of a number option. Without it, the sweep gives the `marks` of the declaration.\n * A declaration without `marks` gets 5 values.\n */\n readonly count?: number;\n /** With `true`, a sweep of a number option on the range also gives the `special` values. The default is `false`. */\n readonly special?: boolean;\n}\n\n/** The number of values of a sweep on a range when the call and the declaration give no number. */\nconst SWEEP_COUNT = 5;\n\n/** This function gives the declarations of a strategy as a record. A strategy without a schema gives an empty record. */\nfunction schemaOf<S extends string>(strategy: DeclaredStrategy<object, S>): Readonly<Record<string, AnyOption<S>>> {\n return (strategy.schema ?? {}) as Readonly<Record<string, AnyOption<S>>>;\n}\n\n/** This function gives the declared options of a strategy, in the order of its schema, with the effective default of each one. */\nexport function listOptions<O extends object, S extends string = string>(\n strategy: DeclaredStrategy<O, S>,\n): { readonly key: keyof O & string; readonly declaration: AnyOption<S>; readonly value: unknown }[] {\n const defaults = optionDefaults(strategy) as Record<string, unknown>;\n return Object.entries(schemaOf(strategy)).map(([key, declaration]) => ({ key: key as keyof O & string, declaration, value: defaults[key] }));\n}\n\n/**\n * This function gives the effective default of each declared option: the value of `defaults`, else the `default` of the declaration.\n * An option without either value stays out. The strategy gives the same output with this object as with no options.\n */\nexport function optionDefaults<O extends object, S extends string = string>(strategy: DeclaredStrategy<O, S>): Partial<O> {\n const out: Record<string, unknown> = {};\n const defaults = strategy.defaults as Readonly<Record<string, unknown>>;\n for (const [key, declaration] of Object.entries(schemaOf(strategy))) {\n if (key in defaults) out[key] = defaults[key];\n else if (declaration.default !== undefined) out[key] = declaration.default;\n }\n for (const key of Object.keys(defaults)) if (!(key in out)) out[key] = defaults[key];\n return out as Partial<O>;\n}\n\n/** This function gives `true` if the number is one of the special values of the declaration. */\nconst isSpecial = (d: NumberOption, v: number): boolean => (d.special ?? []).some((s) => Object.is(s, v) || s === v);\n\n/** This function gives the problem of one value against its declaration, or `undefined` when the value fits. */\nexport function optionIssue(key: string, value: unknown, d: AnyOption): OptionIssue | undefined {\n const issue = (problem: OptionIssue[\"problem\"], message: string): OptionIssue => ({ key, value, problem, message });\n if (value === undefined) return undefined;\n switch (d.type) {\n case \"number\":\n case \"integer\": {\n if (typeof value !== \"number\" || Number.isNaN(value)) return issue(\"type\", `The option \"${key}\" takes a number.`);\n if (isSpecial(d, value)) return undefined;\n if (d.values !== undefined && !d.values.includes(value)) return issue(\"value\", `The option \"${key}\" takes ${d.values.join(\", \")}, not ${value}.`);\n if (value < d.min || value > d.max) return issue(\"range\", `The option \"${key}\" is ${value}, out of the range ${d.min} to ${d.max}.`);\n if (d.type === \"integer\" && !Number.isInteger(value)) return issue(\"integer\", `The option \"${key}\" takes a whole number, not ${value}.`);\n return undefined;\n }\n case \"boolean\":\n return typeof value === \"boolean\" ? undefined : issue(\"type\", `The option \"${key}\" takes true or false.`);\n case \"enum\":\n if (d.custom === \"function\" && typeof value === \"function\") return undefined;\n return d.values.includes(value as string) ? undefined : issue(\"value\", `The option \"${key}\" takes ${d.values.join(\", \")}, not ${String(value)}.`);\n case \"function\":\n return typeof value === \"function\" ? undefined : issue(\"type\", `The option \"${key}\" takes a function.`);\n case \"list\":\n return Array.isArray(value) ? undefined : issue(\"type\", `The option \"${key}\" takes a list.`);\n default:\n return typeof value === \"object\" && value !== null ? undefined : issue(\"type\", `The option \"${key}\" takes an object.`);\n }\n}\n\n/**\n * This function examines option values against the schema of a strategy. It gives one issue for each key without a declaration.\n * It also gives one issue for each value of the wrong type, out of the range, not whole or not one of the values.\n * It changes no value.\n * A strategy without a schema gives no issue.\n */\nexport function checkOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): OptionIssue[] {\n if (strategy.schema === undefined) return [];\n const schema = schemaOf(strategy);\n const out: OptionIssue[] = [];\n for (const [key, value] of Object.entries(options)) {\n const d = schema[key];\n const issue = d ? optionIssue(key, value, d) : { key, value, problem: \"unknown\" as const, message: `The strategy \"${strategy.name}\" has no option \"${key}\".` };\n if (issue) out.push(issue);\n }\n return out;\n}\n\n/**\n * This function moves each number option into the range of its declaration, for a control. An integer option gets the nearest whole number.\n * An option with a list of `values` gets the nearest value of the list. A special value stays. The function keeps each other value as it is. `checkOptions` reports the values that this function cannot correct.\n */\nexport function clampOptions<O extends object>(strategy: DeclaredStrategy<O>, options: Partial<O>): Partial<O> {\n const schema = schemaOf(strategy);\n const out: Record<string, unknown> = { ...options };\n for (const [key, value] of Object.entries(options)) {\n const d = schema[key];\n if (!d || (d.type !== \"number\" && d.type !== \"integer\") || typeof value !== \"number\" || Number.isNaN(value) || isSpecial(d, value)) continue;\n if (d.values !== undefined && d.values.length > 0) {\n out[key] = d.values.reduce((best, v) => (Math.abs(v - value) < Math.abs(best - value) ? v : best));\n continue;\n }\n const v = Math.min(d.max, Math.max(d.min, value));\n out[key] = d.type === \"integer\" ? Math.min(d.max, Math.max(d.min, Math.round(v))) : v;\n }\n return out as Partial<O>;\n}\n\n/** This function gives `count` values from `min` to `max` on the scale, on the grain of the step, without repeats. */\nfunction rangeValues(d: NumberOption, count: number): number[] {\n const n = Math.max(1, Math.floor(count));\n const log = d.scale === \"log\" && d.min > 0;\n const at = (t: number): number => (log ? d.min * (d.max / d.min) ** t : d.min + (d.max - d.min) * t);\n const step = d.step ?? (d.type === \"integer\" ? 1 : undefined);\n const snap = (v: number): number => {\n const s = step === undefined ? v : d.min + Math.round((v - d.min) / step) * step;\n const r = Number(s.toPrecision(12));\n return Math.min(d.max, Math.max(d.min, d.type === \"integer\" ? Math.round(r) : r));\n };\n const out: number[] = [];\n for (let i = 0; i < n; i++) {\n const v = snap(at(n === 1 ? 0 : i / (n - 1)));\n if (!out.includes(v)) out.push(v);\n }\n return out;\n}\n\n/**\n * This function gives the values of a sweep of one option declaration.\n * A number option gives its `marks`, or its list of `values`, or `count` values on its range and its scale. A boolean gives `false` and `true`. An enum gives its values.\n * An option of another type has no sweep, so the function throws an error.\n */\nexport function sweepValues(declaration: AnyOption, options: SweepOptions = {}): unknown[] {\n switch (declaration.type) {\n case \"number\":\n case \"integer\": {\n if (options.count === undefined && declaration.marks !== undefined) return [...declaration.marks];\n if (declaration.values !== undefined) return [...declaration.values];\n const values = rangeValues(declaration, options.count ?? SWEEP_COUNT);\n return options.special ? [...values, ...(declaration.special ?? []).filter((s) => !values.includes(s))] : values;\n }\n case \"boolean\":\n return [false, true];\n case \"enum\":\n return [...declaration.values];\n default:\n throw new Error(`An option of the type \"${declaration.type}\" has no sweep. A sweep takes a number, an integer, a boolean or an enum.`);\n }\n}\n\n/** This function gives the declaration of one option of a strategy. When the strategy has no such declaration, the function throws an error. */\nfunction declarationOf(strategy: DeclaredStrategy, key: string): AnyOption {\n const d = schemaOf(strategy)[key];\n if (!d) throw new Error(`The strategy \"${strategy.name}\" has no declaration of the option \"${key}\".`);\n return d;\n}\n\n/**\n * This function gives the settings of a sweep of one option of a strategy, from its declaration only.\n * The name of each setting is the key and the value, for example `stretch 1.5`.\n */\nexport function sweepOf<O extends object>(strategy: DeclaredStrategy<O>, key: keyof O & string, options: SweepOptions = {}): OptionSetting<O>[] {\n return sweepValues(declarationOf(strategy, key), options).map((value) => ({ name: `${key} ${String(value)}`, options: { [key]: value } as Partial<O> }));\n}\n\n/**\n * This function gives the settings of a grid of options: each combination of the values of the sweeps of the keys.\n * The name of a setting joins the names of its values with commas, in the order of the keys.\n */\nexport function sweepGrid<O extends object>(strategy: DeclaredStrategy<O>, keys: readonly (keyof O & string)[], options: SweepOptions = {}): OptionSetting<O>[] {\n let out: OptionSetting<O>[] = [{ name: \"\", options: {} }];\n for (const key of keys) {\n const sweep = sweepOf(strategy, key, options);\n out = out.flatMap((s) => sweep.map((t) => ({ name: s.name === \"\" ? t.name : `${s.name}, ${t.name}`, options: { ...s.options, ...t.options } })));\n }\n return keys.length === 0 ? [] : out;\n}\n\n/** This function gives the problems of one declaration: a bad range, a bad step, or marks and a default that do not fit. */\nexport function declarationIssues(key: string, d: AnyOption): string[] {\n const out: string[] = [];\n if (d.describe.trim().length === 0) out.push(`The option \"${key}\" has no description.`);\n if (d.type === \"number\" || d.type === \"integer\") {\n if (!Number.isFinite(d.min) || !Number.isFinite(d.max) || d.min > d.max) out.push(`The option \"${key}\" has the bad range ${d.min} to ${d.max}.`);\n if (d.type === \"integer\" && (!Number.isInteger(d.min) || !Number.isInteger(d.max))) out.push(`The integer option \"${key}\" has a range that is not whole.`);\n if (d.step !== undefined && !(d.step > 0)) out.push(`The option \"${key}\" has the step ${d.step}. A step is more than 0.`);\n if (d.scale === \"log\" && !(d.min > 0)) out.push(`The option \"${key}\" has the scale log and the min ${d.min}. The scale log needs a min above 0.`);\n if (d.values !== undefined && d.values.length === 0) out.push(`The number option \"${key}\" has an empty list of values.`);\n for (const v of d.values ?? []) {\n if (v < d.min || v > d.max || (d.type === \"integer\" && !Number.isInteger(v))) out.push(`The value ${v} of the option \"${key}\" is not on its range ${d.min} to ${d.max}.`);\n }\n for (const v of [...(d.marks ?? []), ...(d.default === undefined ? [] : [d.default])]) {\n const issue = optionIssue(key, v, d);\n if (issue) out.push(issue.message);\n }\n } else if (d.type === \"enum\") {\n if (d.values.length === 0) out.push(`The enum option \"${key}\" has no values.`);\n if (d.default !== undefined && !d.values.includes(d.default)) out.push(`The default of the option \"${key}\" is not one of its values.`);\n } else if (d.type === \"boolean\") {\n if (d.default !== undefined && typeof d.default !== \"boolean\") out.push(`The default of the option \"${key}\" is not true or false.`);\n }\n return out;\n}\n\n/**\n * This function examines the schema of a strategy. It gives one message for each option of `defaults` without a declaration.\n * It also gives one message for each bad declaration, and for each value of `defaults` that does not fit its declaration.\n * A good schema gives an empty list.\n */\nexport function checkSchema(strategy: DeclaredStrategy): string[] {\n const schema = schemaOf(strategy);\n const out: string[] = [];\n for (const key of Object.keys(strategy.defaults)) if (!(key in schema)) out.push(`The strategy \"${strategy.name}\" has the option \"${key}\" in its defaults, without a declaration.`);\n for (const [key, d] of Object.entries(schema)) out.push(...declarationIssues(key, d).map((m) => `The strategy \"${strategy.name}\": ${m}`));\n out.push(...checkOptions(strategy, strategy.defaults).filter((i) => i.problem !== \"unknown\").map((i) => `The strategy \"${strategy.name}\": ${i.message}`));\n return out;\n}\n"],"mappings":"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"}
@@ -0,0 +1,251 @@
1
+ import { ClaimBreak } from "./claim.js";
2
+ /** The schema of a strategy space. */
3
+ export declare const STRATEGY_SPACE_SCHEMA = "strategy-space/1";
4
+ /** The kind of a param: a whole number, a real number, one value of a list, or `true` and `false`. */
5
+ export type ParamKind = "int" | "real" | "enum" | "bool";
6
+ /** The value of one param in a config. */
7
+ export type ParamValue = number | string | boolean;
8
+ /** The condition of a conditional param: the param is active only when an earlier param of the same option has one of the values `in`. */
9
+ export interface ParamWhen {
10
+ /** The name of an earlier param of the same option. */
11
+ readonly param: string;
12
+ /** The values of that param that make this param active. */
13
+ readonly in: readonly ParamValue[];
14
+ }
15
+ /** One param of a stage option, with its range. */
16
+ export interface SpaceParam {
17
+ readonly name: string;
18
+ readonly kind: ParamKind;
19
+ /** The smallest value of an `int` or `real` param. */
20
+ readonly min?: number;
21
+ /** The largest value of an `int` or `real` param. */
22
+ readonly max?: number;
23
+ /** The step of the values from `min`. An `int` param has the step 1 by default. */
24
+ readonly step?: number;
25
+ /** The scale of the grid and of the samples. The scale `log` needs a `min` above 0. The default is `linear`. */
26
+ readonly scale?: "linear" | "log";
27
+ /** The values of an `enum` param. */
28
+ readonly values?: readonly (string | number)[];
29
+ /** The value that a config gets when it does not set the param. */
30
+ readonly default: ParamValue;
31
+ /** Without `when`, the param is active each time its option is chosen. */
32
+ readonly when?: ParamWhen;
33
+ }
34
+ /**
35
+ * The rule of the members of a meta option: a count from `min` to `max`. Members with `ordered: false` are sorted by their `configId`.
36
+ * With `min` and `max` 0, the option chooses its members from the input, so a config lists none. This form has no `pool` and no `view`.
37
+ */
38
+ export interface MemberRule {
39
+ readonly min: number;
40
+ readonly max: number;
41
+ readonly ordered: boolean;
42
+ /**
43
+ * The member lists that the option permits. Each entry is a list of whole configs of the space of the members.
44
+ * Without `pool`, each list of base configs with a good count is a member list.
45
+ * With `pool`, a config of the option has the members of one entry, and `spaceSize` counts each entry one time.
46
+ */
47
+ readonly pool?: readonly (readonly StrategyConfig[])[];
48
+ /**
49
+ * The view of the items that the members get. With `bounds`, the option gives its members the bounding box of each item.
50
+ * Then a shape filter checks the members with the kind `rect`, and the option itself with the kinds of the input. Without `view`, the members get the kinds of the input.
51
+ */
52
+ readonly view?: "bounds";
53
+ }
54
+ /** The inputs that an option supports. A missing field supports each value. */
55
+ export interface OptionSupports {
56
+ /** The shape kinds of the boxes, as tags (for example `rect` or `circle`). Without it, the option supports each kind. */
57
+ readonly shape?: readonly string[];
58
+ }
59
+ /** One implementation of a stage. An option with `members` is a meta option: it chooses between whole configs, or wraps them. */
60
+ export interface StageOption {
61
+ readonly id: string;
62
+ readonly params: readonly SpaceParam[];
63
+ readonly members?: MemberRule;
64
+ /** The inputs that the option supports. It describes the option, so it is not part of a config or of its id. */
65
+ readonly supports?: OptionSupports;
66
+ }
67
+ /** The known shape kinds of a check. Without `kinds`, each string that is not empty is a shape kind. */
68
+ export interface KindOptions {
69
+ /** The list of the known shape kinds, for example `SHAPE_KIND_IDS` of `layout-contract`. A kind that is not in the list is a problem. */
70
+ readonly kinds?: readonly string[];
71
+ }
72
+ /** The shape kinds of the inputs of a check, a count or an enumeration. Without `shapes`, each option counts. */
73
+ export interface ShapeFilter extends KindOptions {
74
+ /** The shape kinds of the input. Only the options that support each of these kinds count. */
75
+ readonly shapes?: readonly string[];
76
+ }
77
+ /** One stage of the pipeline of an engine. A config of an `optional` stage can have no option. */
78
+ export interface SpaceStage {
79
+ readonly name: string;
80
+ readonly options: readonly StageOption[];
81
+ readonly optional?: boolean;
82
+ }
83
+ /** A reference to one option of a stage. The option `null` refers to an optional stage that a config does not use. */
84
+ export interface OptionRef {
85
+ readonly stage: string;
86
+ readonly option: string | null;
87
+ }
88
+ /** A rule that keeps out invalid combinations. A config with the option `if` also has the option `then`, or does not have the option `then.not`. */
89
+ export interface SpaceConstraint {
90
+ readonly if: OptionRef;
91
+ readonly then: OptionRef | {
92
+ readonly not: OptionRef;
93
+ };
94
+ }
95
+ /** The strategy space of an engine: the stages, the constraints and the named configs. `docs/STRATEGY-SPACE.md` tells the rules. */
96
+ export interface StrategySpace {
97
+ readonly schema: typeof STRATEGY_SPACE_SCHEMA;
98
+ /** The name of the space. A config names the space with this value. */
99
+ readonly engine: string;
100
+ /** The version of the space. It is not part of a config or of its id. A record writes it. */
101
+ readonly version: string;
102
+ readonly stages: readonly SpaceStage[];
103
+ readonly constraints?: readonly SpaceConstraint[];
104
+ /** The named strategies of the engine, as points of the space. Old records stay comparable through them. */
105
+ readonly named?: Readonly<Record<string, StrategyConfig>>;
106
+ /** The spaces of a product space, from `productSpace`. A meta option of a part takes members of that part. */
107
+ readonly parts?: readonly StrategySpace[];
108
+ }
109
+ /** The choice of one stage in a config: an option, its params and, for a meta option, its members. */
110
+ export interface StageChoice {
111
+ readonly option: string;
112
+ /** The values of the active params. A param that is not set gets its default. */
113
+ readonly params?: Readonly<Record<string, ParamValue>>;
114
+ /** The members of a meta option: configs of the same space, with no meta option. */
115
+ readonly members?: readonly StrategyConfig[];
116
+ }
117
+ /** One point of a strategy space. The value `null` of a stage tells that the config does not use the optional stage. */
118
+ export interface StrategyConfig {
119
+ /** The `engine` of the space, without its version. */
120
+ readonly space: string;
121
+ readonly stages: Readonly<Record<string, StageChoice | null>>;
122
+ }
123
+ /** The count of the grid points of a space. */
124
+ export interface SpaceSize {
125
+ /** The count of the configs without a meta option. */
126
+ readonly base: bigint;
127
+ /** The count of the configs with a meta option. The value is `null` when the count is more than `maxMeta`. */
128
+ readonly meta: bigint | null;
129
+ }
130
+ /** The options of `spaceSize`. */
131
+ export interface SpaceSizeOptions extends ShapeFilter {
132
+ /** The count of the levels of each numeric param, a whole number of 1 or more. */
133
+ readonly levels: number;
134
+ /** The largest meta count that `spaceSize` gives. The default is `10n ** 18n`. */
135
+ readonly maxMeta?: bigint;
136
+ }
137
+ /** The options of a grid. The grid gives only the configs without a meta option. */
138
+ export interface GridOptions extends ShapeFilter {
139
+ readonly mode: "grid";
140
+ readonly levels: number;
141
+ /** The largest count of configs. A larger grid causes a `RangeError`. The default is 100000. */
142
+ readonly limit?: number;
143
+ }
144
+ /** The options of a sample. */
145
+ export interface SampleOptions extends ShapeFilter {
146
+ readonly mode: "sample";
147
+ /** The count of the configs, a whole number of 0 or more. */
148
+ readonly n: number;
149
+ readonly seed: number;
150
+ /** With `skip` (the default), the sample has no meta option. With `sample`, a meta option draws its members from base samples, or one entry of its `pool`. */
151
+ readonly meta?: "skip" | "sample";
152
+ /** The largest count of option draws. The default is the larger value of 1000 and `100 * n`. */
153
+ readonly maxTries?: number;
154
+ /**
155
+ * How a point keeps the constraints. With `reject` (the default), a point that breaks a constraint draws again.
156
+ * With `walk`, a point draws its stages from the largest to the smallest. Each stage draws only from the options that keep the constraints with the stages before it.
157
+ * The stages of `fixed` draw before the other stages.
158
+ * A space whose constraints reject most draws gets its sample with `walk`. The two values give different configs for one seed.
159
+ * A sample of `walk` is not uniform over the valid configs. Each option of the largest stage gets an equal share, whatever count of valid configs it has.
160
+ * Thus a mean over this sample is not a mean of the space.
161
+ */
162
+ readonly draw?: "reject" | "walk";
163
+ /**
164
+ * The options that each config of the sample has. A key is the name of a stage, and its value is the id of one option of that stage. The other stages draw as before.
165
+ * A stage of a product space has the name of its part before its own name, for example `pack.placement`.
166
+ * A fixed meta option needs `meta: "sample"`. A meta option of another stage of the same part goes out, because such an option replaces the fixed stage.
167
+ * A fixed option that the shape filter removes, or that the constraints forbid, gets a `RangeError` with the reason.
168
+ */
169
+ readonly fixed?: Readonly<Record<string, string>>;
170
+ }
171
+ /** The options of `enumerateSpace`. */
172
+ export type EnumerateOptions = GridOptions | SampleOptions;
173
+ /** This function gives the problem of a value of a param, or `undefined` for a value in its range. The corpus dimensions of `families.ts` use it. */
174
+ export declare function paramProblem(param: SpaceParam, value: unknown): string | undefined;
175
+ /** This function tells if a param is active for the values of the earlier params. A param without `when` is always active. */
176
+ export declare function paramActive(param: SpaceParam, values: Readonly<Record<string, ParamValue>>): boolean;
177
+ /** This function gives the stable form of a value of a param: a real number gets 12 significant digits, and `-0` becomes `0`. */
178
+ export declare function paramValue(param: SpaceParam, value: ParamValue): ParamValue;
179
+ /**
180
+ * This function checks one config of a space. It gives one break for each problem. An empty list tells that the config is good.
181
+ * The `claim` of a break is the kind of the problem, for example `missing`, `unknown`, `inactive`, `param`, `constraint`, `members` or `depth`.
182
+ *
183
+ * A member list that is not an entry of the `pool` of its option gives the claim `pool`.
184
+ * With `shapes`, an option that does not support a kind of the list gives the claim `shape`. A member of a meta option gets the same check.
185
+ * A member of an option with `view: "bounds"` gets the check with the kind `rect`.
186
+ */
187
+ export declare function checkConfig(space: StrategySpace, config: StrategyConfig, options?: ShapeFilter): ClaimBreak[];
188
+ /**
189
+ * This function checks a space. It gives one break for each problem. An empty list tells that the space is good.
190
+ * It finds duplicate names, empty ranges, defaults out of range, enums without values, bad log scales, unknown names and bad named configs.
191
+ * It also checks each entry of a `pool`: the count of its members, each member and a repeated entry.
192
+ * The `inputs` of a break of a named config have the name of the config.
193
+ * With `kinds`, a shape kind of a `supports` that is not in the list gives the claim `shape`.
194
+ */
195
+ export declare function checkSpace(space: StrategySpace, kindOptions?: KindOptions): ClaimBreak[];
196
+ /**
197
+ * This function gives the canonical form of a config: each stage of the space, the active params only, the defaults, and stable numbers.
198
+ * An unused optional stage is `null`. The members of an unordered meta option are sorted by their `configId`.
199
+ * It throws an `Error` for a config with a break of `checkConfig`.
200
+ */
201
+ export declare function canonicalConfig(space: StrategySpace, config: StrategyConfig): StrategyConfig;
202
+ /**
203
+ * This function gives the id of a config: the first 16 hex characters of the SHA-256 of `canonicalJson` of `canonicalConfig`.
204
+ * Two spellings of one config get one id. The version of the space is not part of the id.
205
+ */
206
+ export declare function configId(space: StrategySpace, config: StrategyConfig): string;
207
+ /**
208
+ * This function gives the grid values of one param, in increasing order. An `enum` gives its values, and a `bool` gives `false` and `true`.
209
+ * With 1 level, a numeric param gives its default. With k levels, it gives k points from `min` to `max` on its scale and step, without repeats.
210
+ */
211
+ export declare function paramLevels(param: SpaceParam, levels: number): ParamValue[];
212
+ /**
213
+ * This function gives the count of the grid points of a space, with `levels` levels for each numeric param.
214
+ * It counts only the active params and the combinations that keep the constraints. It gives the meta configs apart.
215
+ * A meta option with a `pool` counts each entry of the pool one time.
216
+ * With `shapes`, it counts only the options that support each kind of the list.
217
+ */
218
+ export declare function spaceSize(space: StrategySpace, options: SpaceSizeOptions): SpaceSize;
219
+ /**
220
+ * This function gives configs of a space, in their canonical form. Each config keeps the constraints. The result is deterministic.
221
+ *
222
+ * - The mode `grid` gives each grid point without a meta option, in a stable order. Stages, options and values go in the order of the space.
223
+ * - The mode `sample` gives `n` configs of a seeded Latin hypercube. The options of a stage get equal strata, and each active param gets its own strata.
224
+ * - A sample draws again when a point breaks a constraint. After `maxTries` draws, it throws an `Error`. Two sampled configs can be equal.
225
+ * - With `draw: "walk"`, each stage draws only from the options that keep the constraints with the stages before it. The largest stage keeps its equal strata.
226
+ * - With `shapes`, only the options that support each kind of the list count. Without `shapes`, the result does not change.
227
+ * - A meta option with a `pool` gives only the entries of its pool. With `shapes`, only the entries whose members support each kind count.
228
+ * - A sample draws again when a point chooses a meta option without a member list. `spaceSize` gives such an option 0.
229
+ * - With `shapes`, a sample throws a `RangeError` when the space has no config for the kinds. The error names the stages without an option and the meta options without members.
230
+ * - With `fixed`, each config has the fixed option at each named stage. A fixed option that the space, the shape filter or the constraints do not permit gets a `RangeError` with the reason.
231
+ */
232
+ export declare function enumerateSpace(space: StrategySpace, options: EnumerateOptions): StrategyConfig[];
233
+ /**
234
+ * This function gives the shape kinds that a space accepts. The value comes from the `supports` of the options.
235
+ * A space accepts a kind when it has at least one config without a meta option on that kind.
236
+ * With `kinds`, it examines the kinds of that list, in its order. Without `kinds`, it examines the kinds that the `supports` of the space name, in the order of their first use.
237
+ */
238
+ export declare function spaceShapeKinds(space: StrategySpace, options?: KindOptions): string[];
239
+ /**
240
+ * This function joins configs of the parts of a product space into one config of the space `space`.
241
+ * The name of each stage gets the prefix `<space of the part>.`. The members of a meta option do not change.
242
+ */
243
+ export declare function joinConfigs(space: string, parts: readonly StrategyConfig[]): StrategyConfig;
244
+ /** This function gives the config of the part `engine` of a joint config. It throws a `RangeError` when no stage has the prefix `<engine>.`. */
245
+ export declare function splitConfig(config: StrategyConfig, engine: string): StrategyConfig;
246
+ /**
247
+ * This function joins two spaces, for example a packer and a router, into the space `name`. The stages of `a` and `b` get the prefixes `<a.engine>.` and `<b.engine>.`.
248
+ * The constraints get the same prefixes. The named configs are the pairs `<name of a>+<name of b>`. It throws a `RangeError` when the two engines have the same name.
249
+ */
250
+ export declare function productSpace(a: StrategySpace, b: StrategySpace, name: string): StrategySpace;
251
+ //# sourceMappingURL=strategy-space.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy-space.d.ts","names":[],"sources":["../src/strategy-space.ts"],"mappings":";;qBAMa;;YASD;;YAGA;;iBAGK;;WAEN;;WAEA,aAAa;;;iBAIP;WACN;WACA,MAAM;;WAEN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA,SAAS;;WAET,OAAO;;;;;;iBAOD;WACN;WACA;WACA;;;;;;WAMA,0BAA0B;;;;;WAK1B;;;iBAIM;;WAEN;;;iBAIM;WACN;WACA,iBAAiB;WACjB,UAAU;;WAEV,WAAW;;;iBAIL;;WAEN;;;iBAIM,oBAAoB;;WAE1B;;;iBAIM;WACN;WACA,kBAAkB;WAClB;;;iBAIM;WACN;WACA;;;iBAIM;WACN,IAAI;WACJ,MAAM;aAAuB,KAAK;;;;iBAI5B;WACN,eAAe;;WAEf;;WAEA;WACA,iBAAiB;WACjB,uBAAuB;;WAEvB,QAAQ,SAAS,eAAe;;WAEhC,iBAAiB;;;iBAIX;WACN;;WAEA,SAAS,SAAS,eAAe;;WAEjC,mBAAmB;;;iBAIb;;WAEN;WACA,QAAQ,SAAS,eAAe;;;iBAI1B;;WAEN;;WAEA;;;iBAIM,yBAAyB;;WAE/B;;WAEA;;;iBAIM,oBAAoB;WAC1B;WACA;;WAEA;;;iBAIM,sBAAsB;WAC5B;;WAEA;WACA;;WAEA;;WAEA;;;;;;;;;WASA;;;;;;;WAOA,QAAQ,SAAS;;;YAIhB,mBAAmB,cAAc;;wBAiD7B,aAAa,OAAO,YAAY;;wBAKhC,YAAY,OAAO,YAAY,QAAQ,SAAS,eAAe;;wBAK/D,WAAW,OAAO,YAAY,OAAO,aAAa;;;;;;;;;wBAgRlD,YAAY,OAAO,eAAe,QAAQ,gBAAgB,UAAS,cAAmB;;;;;;;;wBAWtF,WAAW,OAAO,eAAe,cAAa,cAAmB;;;;;;wBA4EjE,gBAAgB,OAAO,eAAe,QAAQ,iBAAiB;;;;;wBAU/D,SAAS,OAAO,eAAe,QAAQ;;;;;wBA4CvC,YAAY,OAAO,YAAY,iBAAiB;;;;;;;wBA6JhD,UAAU,OAAO,eAAe,SAAS,mBAAmB;;;;;;;;;;;;;;wBA8U5D,eAAe,OAAO,eAAe,SAAS,mBAAmB;;;;;;wBAkCjE,gBAAgB,OAAO,eAAe,UAAS;;;;;wBAU/C,YAAY,eAAe,gBAAgB,mBAAmB;;wBAO9D,YAAY,QAAQ,gBAAgB,iBAAiB;;;;;wBAWrD,aAAa,GAAG,eAAe,GAAG,eAAe,eAAe"}