@calcit/procs 0.13.42 → 0.13.44

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.
Files changed (68) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/RFCs/04-15-match-syntax-rfc.md +14 -8
  3. package/RFCs/04-15-type-directed-optimization-catalog.md +20 -14
  4. package/RFCs/08-05-systematic-nil-reduction-rfc.md +16 -5
  5. package/editing-history/20260823-0401-guard-range-progress.md +9 -0
  6. package/editing-history/20260824-0318-range-backend-consistency.md +13 -0
  7. package/editing-history/20260824-0645-unit-truthiness-review.md +6 -0
  8. package/editing-history/20260824-0655-project-module-merge-review.md +4 -0
  9. package/editing-history/20260824-0705-unit-effect-return-review.md +5 -0
  10. package/editing-history/20260824-1540-scaffold-data-kind-schema.md +6 -0
  11. package/editing-history/20260824-2119-validate-number-call-metadata.md +8 -0
  12. package/editing-history/20260824-2155-stable-js-struct-layout.md +11 -0
  13. package/editing-history/20260824-2224-struct-index-review-followups.md +7 -0
  14. package/editing-history/20260824-2233-indexed-struct-js-regressions.md +5 -0
  15. package/editing-history/20260824-2256-dynamic-method-analysis.md +9 -0
  16. package/editing-history/20260824-2329-dynamic-method-review-followups.md +7 -0
  17. package/editing-history/202608241525-release-01343.md +5 -0
  18. package/editing-history/202608241731-contiguous-executable-calls.md +34 -0
  19. package/editing-history/202608241920-borrowed-syntax-arguments.md +34 -0
  20. package/editing-history/202608241924-unify-editing-history-directory.md +7 -0
  21. package/editing-history/202608242013-typed-native-number-ops.md +34 -0
  22. package/editing-history/20260825-0020-indexed-enum-match.md +34 -0
  23. package/editing-history/20260825-0100-indexed-enum-match-validation.md +7 -0
  24. package/editing-history/20260825-0120-indexed-enum-match-review.md +7 -0
  25. package/editing-history/20260825-1215-typed-literal-paths.md +56 -0
  26. package/editing-history/20260825-1344-typed-literal-path-review.md +17 -0
  27. package/editing-history/20260825-1505-typed-method-native-op.md +23 -0
  28. package/editing-history/20260825-1540-nil-unit-runtime-separation.md +15 -0
  29. package/editing-history/20260825-1725-typed-method-error-stack-review.md +10 -0
  30. package/editing-history/20260825-1733-nil-unit-doc-review.md +5 -0
  31. package/editing-history/20260825-1758-nil-unit-review-followup.md +6 -0
  32. package/editing-history/20260825-1845-release-01344.md +6 -0
  33. package/examples/bench_enum_match.rs +41 -0
  34. package/lib/calcit-data.d.mts +4 -0
  35. package/lib/calcit-data.mjs +59 -9
  36. package/lib/calcit.procs.d.mts +17 -9
  37. package/lib/calcit.procs.mjs +104 -57
  38. package/lib/custom-formatter.mjs +12 -3
  39. package/lib/js-cirru.mjs +6 -5
  40. package/lib/js-impl.mjs +4 -3
  41. package/lib/js-primes.d.mts +1 -1
  42. package/lib/js-primes.mjs +22 -18
  43. package/lib/js-struct-def.mjs +4 -6
  44. package/lib/js-struct-value.d.mts +5 -1
  45. package/lib/js-struct-value.mjs +66 -38
  46. package/lib/package.json +8 -2
  47. package/package.json +8 -2
  48. package/ts-src/calcit-data.mts +54 -9
  49. package/ts-src/calcit.procs.mts +107 -61
  50. package/ts-src/custom-formatter.mts +12 -3
  51. package/ts-src/js-cirru.mts +6 -5
  52. package/ts-src/js-impl.mts +4 -3
  53. package/ts-src/js-primes.mts +5 -1
  54. package/ts-src/js-struct-def.mts +4 -6
  55. package/ts-src/js-struct-value.mts +78 -33
  56. /package/{history → editing-history}/202608191501-release-01326.md +0 -0
  57. /package/{history → editing-history}/202608191628-revert-transparent-union-types.md +0 -0
  58. /package/{history → editing-history}/202608191741-release-01327.md +0 -0
  59. /package/{history → editing-history}/202608212020-release-01328.md +0 -0
  60. /package/{2026082200-release-01330.md → editing-history/2026082200-release-01330.md} +0 -0
  61. /package/{2026082200-same-package-module-meta.md → editing-history/2026082200-same-package-module-meta.md} +0 -0
  62. /package/{2026082200-transitive-module-loading.md → editing-history/2026082200-transitive-module-loading.md} +0 -0
  63. /package/{2026082222-release-01331.md → editing-history/2026082222-release-01331.md} +0 -0
  64. /package/{history → editing-history}/2026082223-release-01332.md +0 -0
  65. /package/{history → editing-history}/2026082223-same-package-namespaces.md +0 -0
  66. /package/{history → editing-history}/2026082300-fix-cross-package-namespace-merge.md +0 -0
  67. /package/{history → editing-history}/2026082300-release-01333.md +0 -0
  68. /package/{history → editing-history}/2026082316-release-01334.md +0 -0
@@ -1,23 +1,22 @@
1
1
  import { CalcitImpl } from "./js-impl.mjs";
2
- import { newTag, castTag, toString, getStringName, findInFields } from "./calcit-data.mjs";
2
+ import { newTag, castTag, toString, getStringName, findInFields, compareTagNames, canonicalizeTagPairs, tagNamesAreCanonical, } from "./calcit-data.mjs";
3
3
  import { CalcitMap, CalcitSliceMap } from "./js-map.mjs";
4
4
  import { CalcitStructDef } from "./js-struct-def.mjs";
5
5
  export class CalcitStructValue {
6
6
  constructor(name, fields, values, structRef) {
7
7
  this.name = name;
8
- let fieldNames = fields.map(castTag);
9
- this.fields = fields;
10
- if (values != null) {
11
- if (values.length !== fields.length) {
12
- throw new Error("fields/values length not match");
13
- }
14
- this.values = values;
15
- }
16
- else {
17
- this.values = new Array(fieldNames.length);
18
- }
8
+ const fieldNames = fields.map(castTag);
9
+ const sourceValues = values ?? new Array(fieldNames.length);
10
+ const [canonicalFields, canonicalValues] = canonicalizeTagPairs(fieldNames, sourceValues, "CalcitStructValue");
11
+ this.fields = canonicalFields;
12
+ this.values = canonicalValues;
19
13
  this.cachedHash = null;
20
- this.structRef = structRef || new CalcitStructDef(name, fields, new Array(fields.length).fill(null));
14
+ this.structRef =
15
+ structRef == null
16
+ ? new CalcitStructDef(name, canonicalFields, new Array(canonicalFields.length).fill(null))
17
+ : tagNamesAreCanonical(structRef.fields)
18
+ ? structRef
19
+ : new CalcitStructDef(structRef.name, structRef.fields, structRef.fieldTypes, structRef.impls);
21
20
  }
22
21
  get(k) {
23
22
  let field = castTag(k);
@@ -58,6 +57,40 @@ export class CalcitStructValue {
58
57
  }
59
58
  return new CalcitStructValue(this.name, this.fields, values, this.structRef);
60
59
  }
60
+ nthAt(index, field) {
61
+ const idx = checkedStructIndex(index, "&struct:nth");
62
+ this.assertFieldAt(idx, field, "&struct:nth");
63
+ return this.values[idx];
64
+ }
65
+ assocAt(index, field, value) {
66
+ const idx = checkedStructIndex(index, "&struct:assoc-at");
67
+ this.assertFieldAt(idx, field, "&struct:assoc-at");
68
+ const values = this.values.slice();
69
+ values[idx] = value;
70
+ return new CalcitStructValue(this.name, this.fields, values, this.structRef);
71
+ }
72
+ withAt(...triples) {
73
+ if (triples.length === 0 || triples.length % 3 !== 0) {
74
+ throw new Error("&struct:with-at expected index/tag/value triples");
75
+ }
76
+ const values = this.values.slice();
77
+ for (let base = 0; base < triples.length; base += 3) {
78
+ const idx = checkedStructIndex(triples[base], "&struct:with-at");
79
+ this.assertFieldAt(idx, triples[base + 1], "&struct:with-at");
80
+ values[idx] = triples[base + 2];
81
+ }
82
+ return new CalcitStructValue(this.name, this.fields, values, this.structRef);
83
+ }
84
+ assertFieldAt(idx, field, operation) {
85
+ if (idx >= this.fields.length) {
86
+ throw new Error(`${operation} index ${idx} out of range for struct '${this.name.value}' with ${this.fields.length} fields`);
87
+ }
88
+ const fieldTag = castTag(field);
89
+ const expectedTag = this.fields[idx];
90
+ if (expectedTag.value !== fieldTag.value) {
91
+ throw new Error(`${operation} index ${idx} expects field :${expectedTag.value}, but received :${fieldTag.value}`);
92
+ }
93
+ }
61
94
  /** return -1 for missing */
62
95
  findIndex(k) {
63
96
  let field = castTag(k);
@@ -93,37 +126,19 @@ export class CalcitStructValue {
93
126
  }
94
127
  }
95
128
  export let new_struct_value = (name, ...fields) => {
96
- let fieldNames = fields.map(castTag).sort((x, y) => {
97
- if (x.idx < y.idx) {
98
- return -1;
99
- }
100
- else if (x.idx > y.idx) {
101
- return 1;
102
- }
103
- else {
104
- throw new Error(`Unexpected duplication in struct fields: ${x.toString()}`);
105
- }
106
- });
129
+ let fieldNames = fields.map(castTag).sort(compareTagNames);
130
+ assertUniqueFields(fieldNames, "Unexpected duplication in struct fields");
107
131
  return new CalcitStructValue(castTag(name), fieldNames);
108
132
  };
109
133
  export let new_impl_struct_value = (impl, name, ...fields) => {
110
- let fieldNames = fields.map(castTag).sort((x, y) => {
111
- if (x.idx < y.idx) {
112
- return -1;
113
- }
114
- else if (x.idx > y.idx) {
115
- return 1;
116
- }
117
- else {
118
- throw new Error(`Unexpected duplication in struct fields: ${x.toString()}`);
119
- }
120
- });
134
+ let fieldNames = fields.map(castTag).sort(compareTagNames);
135
+ assertUniqueFields(fieldNames, "Unexpected duplication in struct fields");
121
136
  let nameTag = castTag(name);
122
137
  let structRef = new CalcitStructDef(nameTag, fieldNames, new Array(fieldNames.length).fill(null), [impl]);
123
138
  return new CalcitStructValue(nameTag, fieldNames, undefined, structRef);
124
139
  };
125
140
  /** Loose record: `?{} :field1 val1 :field2 val2` – record without a declared struct.
126
- * Fields are sorted by tag index. The record name is "?" (sentinel). */
141
+ * Fields use the same lexical layout as named structs. The record name is "_". */
127
142
  export let _$q__$M_ = (...xs) => {
128
143
  if (xs.length % 2 !== 0) {
129
144
  throw new Error("?{} expected pairs of :field value");
@@ -132,10 +147,10 @@ export let _$q__$M_ = (...xs) => {
132
147
  for (let i = 0; i < xs.length; i += 2) {
133
148
  pairs.push([castTag(xs[i]), xs[i + 1]]);
134
149
  }
135
- pairs.sort((a, b) => a[0].idx - b[0].idx);
150
+ pairs.sort((a, b) => compareTagNames(a[0], b[0]));
136
151
  // Check for duplicate fields after sorting
137
152
  for (let i = 1; i < pairs.length; i++) {
138
- if (pairs[i][0].idx === pairs[i - 1][0].idx) {
153
+ if (pairs[i][0].value === pairs[i - 1][0].value) {
139
154
  throw new Error(`?{} received duplicate field: :${getStringName(pairs[i][0])}`);
140
155
  }
141
156
  }
@@ -144,6 +159,19 @@ export let _$q__$M_ = (...xs) => {
144
159
  let looseTag = newTag("_");
145
160
  return new CalcitStructValue(looseTag, fieldNames, values);
146
161
  };
162
+ function checkedStructIndex(value, operation) {
163
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
164
+ throw new Error(`${operation} expected a non-negative integer index, but received: ${value}`);
165
+ }
166
+ return value;
167
+ }
168
+ function assertUniqueFields(fields, message) {
169
+ for (let idx = 1; idx < fields.length; idx++) {
170
+ if (fields[idx - 1].value === fields[idx].value) {
171
+ throw new Error(`${message}: ${fields[idx].toString()}`);
172
+ }
173
+ }
174
+ }
147
175
  export let fieldsEqual = (xs, ys) => {
148
176
  if (xs === ys) {
149
177
  return true; // special case, referential equal
package/lib/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.42",
3
+ "version": "0.13.44",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
@@ -17,9 +17,15 @@
17
17
  "test-fail": "cargo test -q --bin calcit type_fail_",
18
18
  "test-snippets": "cargo test -q snippets::tests",
19
19
  "check-js-runtime": "node scripts/check-js-runtime-identity.mjs",
20
+ "check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
21
+ "check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
20
22
  "bench-recur-smoke": "cargo run --bin calcit -- calcit/test.cirru js && node --input-type=module -e \"import { test_loop } from './js-out/test-recursion.main.mjs'; const n=3000; const t0=process.hrtime.bigint(); for(let i=0;i<n;i++) test_loop(); const dt=Number(process.hrtime.bigint()-t0)/1e6; console.log('test_loop_ms='+dt.toFixed(3));\"",
23
+ "bench-enum-match": "cargo run --release --example bench_enum_match && yarn compile && node scripts/bench-enum-match.mjs",
24
+ "bench-literal-paths": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/bench-literal-paths.cirru && node scripts/bench-literal-paths-native.mjs && target/release/calcit --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
25
+ "bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
26
+ "bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
21
27
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
22
- "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
28
+ "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
23
29
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
24
30
  "try-all": "yarn check-all",
25
31
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calcit/procs",
3
- "version": "0.13.42",
3
+ "version": "0.13.44",
4
4
  "main": "./lib/calcit.procs.mjs",
5
5
  "devDependencies": {
6
6
  "@types/node": "^25.7.0",
@@ -17,9 +17,15 @@
17
17
  "test-fail": "cargo test -q --bin calcit type_fail_",
18
18
  "test-snippets": "cargo test -q snippets::tests",
19
19
  "check-js-runtime": "node scripts/check-js-runtime-identity.mjs",
20
+ "check-literal-paths": "cargo run --bin calcit -- calcit/bench-literal-paths.cirru && cargo run --bin calcit -- --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
21
+ "check-typed-method-rem": "cargo run --bin calcit -- calcit/fibo.cirru && cargo run --bin calcit -- --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
20
22
  "bench-recur-smoke": "cargo run --bin calcit -- calcit/test.cirru js && node --input-type=module -e \"import { test_loop } from './js-out/test-recursion.main.mjs'; const n=3000; const t0=process.hrtime.bigint(); for(let i=0;i<n;i++) test_loop(); const dt=Number(process.hrtime.bigint()-t0)/1e6; console.log('test_loop_ms='+dt.toFixed(3));\"",
23
+ "bench-enum-match": "cargo run --release --example bench_enum_match && yarn compile && node scripts/bench-enum-match.mjs",
24
+ "bench-literal-paths": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/bench-literal-paths.cirru && node scripts/bench-literal-paths-native.mjs && target/release/calcit --emit-path target/literal-path-js calcit/bench-literal-paths.cirru js && node scripts/bench-literal-paths.mjs",
25
+ "bench-typed-method-rem": "cargo build --release --bin calcit && yarn compile && target/release/calcit calcit/fibo.cirru && node scripts/bench-typed-method-rem-native.mjs && target/release/calcit --emit-path target/typed-method-rem-js calcit/fibo.cirru js && node scripts/bench-typed-method-rem.mjs",
26
+ "bench-struct-index": "yarn compile && node scripts/bench-struct-index.mjs",
21
27
  "check-smooth": "yarn fmt-rs && yarn lint-rs && yarn test-rs && yarn check-all",
22
- "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm",
28
+ "check-all": "yarn compile && yarn check-js-runtime && yarn check-agent-interface && yarn try-core-tests && yarn try-rs && yarn try-js && yarn try-ir && yarn try-wasm && yarn check-literal-paths && yarn check-typed-method-rem",
23
29
  "check-agent-interface": "cargo build --bin calcit && node scripts/check-agent-interface.mjs",
24
30
  "try-all": "yarn check-all",
25
31
  "try-core-tests": "cargo run --bin calcit -- src/cirru/calcit-core.cirru test --tag unit --summary-only",
@@ -126,6 +126,49 @@ export let getStringName = (x: CalcitValue): string => {
126
126
  throw new Error("Cannot get string as name");
127
127
  };
128
128
 
129
+ /** Compare tag names by Unicode scalar value, matching Rust `str` ordering. */
130
+ export function compareTagNames(x: CalcitTag, y: CalcitTag): number {
131
+ let xIdx = 0;
132
+ let yIdx = 0;
133
+ while (xIdx < x.value.length && yIdx < y.value.length) {
134
+ const xCode = x.value.codePointAt(xIdx)!;
135
+ const yCode = y.value.codePointAt(yIdx)!;
136
+ if (xCode < yCode) return -1;
137
+ if (xCode > yCode) return 1;
138
+ xIdx += xCode > 0xffff ? 2 : 1;
139
+ yIdx += yCode > 0xffff ? 2 : 1;
140
+ }
141
+ if (xIdx < x.value.length) return 1;
142
+ if (yIdx < y.value.length) return -1;
143
+ return 0;
144
+ }
145
+
146
+ export function tagNamesAreCanonical(fields: CalcitTag[]): boolean {
147
+ for (let idx = 1; idx < fields.length; idx++) {
148
+ if (compareTagNames(fields[idx - 1], fields[idx]) >= 0) return false;
149
+ }
150
+ return true;
151
+ }
152
+
153
+ export function canonicalizeTagPairs<T>(fields: CalcitTag[], values: T[], context: string): [CalcitTag[], T[]] {
154
+ if (fields.length !== values.length) {
155
+ throw new Error(`${context}: fields and values length mismatch`);
156
+ }
157
+ if (tagNamesAreCanonical(fields)) return [fields, values];
158
+
159
+ const pairs = fields.map((field, idx) => [field, values[idx]] as [CalcitTag, T]);
160
+ pairs.sort((a, b) => compareTagNames(a[0], b[0]));
161
+ for (let idx = 1; idx < pairs.length; idx++) {
162
+ if (pairs[idx - 1][0].value === pairs[idx][0].value) {
163
+ throw new Error(`${context}: duplicated field :${pairs[idx][0].value}`);
164
+ }
165
+ }
166
+ return [
167
+ pairs.map(([field]) => field),
168
+ pairs.map(([, value]) => value),
169
+ ];
170
+ }
171
+
129
172
  /** returns -1 when not found */
130
173
  export function findInFields(xs: Array<CalcitTag>, y: CalcitTag): number {
131
174
  let low = 0;
@@ -134,9 +177,10 @@ export function findInFields(xs: Array<CalcitTag>, y: CalcitTag): number {
134
177
  while (low <= high) {
135
178
  const mid = Math.floor((low + high) / 2);
136
179
  const midVal = xs[mid];
137
- if (midVal.idx < y.idx) {
180
+ const ordering = compareTagNames(midVal, y);
181
+ if (ordering < 0) {
138
182
  low = mid + 1;
139
- } else if (midVal.idx > y.idx) {
183
+ } else if (ordering > 0) {
140
184
  high = mid - 1;
141
185
  } else {
142
186
  return mid;
@@ -180,6 +224,7 @@ export let castTag = (x: CalcitValue): CalcitTag => {
180
224
  export var refsRegistry = new Map<string, CalcitRef>();
181
225
 
182
226
  let defaultHash_nil = valueHash("nil:");
227
+ let defaultHash_unit = valueHash("unit:");
183
228
  let defaultHash_number = valueHash("number:");
184
229
  let defaultHash_string = valueHash("string:");
185
230
  let defaultHash_tag = valueHash("tag:");
@@ -204,9 +249,10 @@ let fnHashCounter = 0;
204
249
  let jsObjectHashCounter = 0;
205
250
 
206
251
  export let hashFunction = (x: CalcitValue): Hash => {
207
- if (x == null) {
252
+ if (x === null) {
208
253
  return defaultHash_nil;
209
254
  }
255
+ if (x === undefined) return defaultHash_unit;
210
256
  if (typeof x === "number") {
211
257
  return mergeValueHash(defaultHash_number, x);
212
258
  }
@@ -398,9 +444,10 @@ let hashCirru = (base: number, x: CirruWriterNode) => {
398
444
  overwriteHashGenerator(hashFunction);
399
445
 
400
446
  export let toString = (x: CalcitValue, escaped: boolean, disableJsDataWarning: boolean = false): string => {
401
- if (x == null) {
447
+ if (x === null) {
402
448
  return "nil";
403
449
  }
450
+ if (x === undefined) return "&unit";
404
451
  if (typeof x === "string") {
405
452
  if (escaped) {
406
453
  // turn to visual string representation
@@ -505,9 +552,10 @@ export let to_js_data = (x: CalcitValue, options?: CalcitValue | boolean): any =
505
552
  };
506
553
 
507
554
  let to_js_data_inner = (x: CalcitValue, addColon: boolean): any => {
508
- if (x == null) {
555
+ if (x === null) {
509
556
  return null;
510
557
  }
558
+ if (x === undefined) return undefined;
511
559
  if (x === true || x === false) {
512
560
  return x;
513
561
  }
@@ -596,10 +644,7 @@ export let _$n__$e_ = (x: CalcitValue, y: CalcitValue): boolean => {
596
644
  if (x === y) {
597
645
  return true;
598
646
  }
599
- if (x == null) {
600
- if (y == null) {
601
- return true;
602
- }
647
+ if (x === null) {
603
648
  return false;
604
649
  }
605
650