@suss/datalog 0.38.0 → 0.38.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.
package/dist/index.cjs CHANGED
@@ -34,6 +34,7 @@ __export(index_exports, {
34
34
  formatProfile: () => formatProfile,
35
35
  lit: () => lit,
36
36
  notLit: () => notLit,
37
+ noteReads: () => noteReads,
37
38
  profileEvaluation: () => profileEvaluation,
38
39
  profileEvaluationAsync: () => profileEvaluationAsync,
39
40
  proofOf: () => proofOf,
@@ -762,7 +763,7 @@ function addToBucket(buckets, key, tuple) {
762
763
  bucket.push(tuple);
763
764
  }
764
765
  }
765
- function bucketIn(relation, column, value) {
766
+ function columnIndex(relation, column) {
766
767
  let buckets = relation.columns[column];
767
768
  if (buckets === void 0) {
768
769
  buckets = /* @__PURE__ */ new Map();
@@ -774,7 +775,279 @@ function bucketIn(relation, column, value) {
774
775
  }
775
776
  relation.columns[column] = buckets;
776
777
  }
777
- return tuplesIn(buckets.get(value));
778
+ return buckets;
779
+ }
780
+ function bucketIn(relation, column, value) {
781
+ return tuplesIn(columnIndex(relation, column).get(value));
782
+ }
783
+ function bucketSize(relation, column, value) {
784
+ const bucket = columnIndex(relation, column).get(value);
785
+ if (bucket === void 0) {
786
+ return 0;
787
+ }
788
+ return isLone(bucket) ? 1 : bucket.length;
789
+ }
790
+
791
+ // src/reads.ts
792
+ function noteIn(reads, column, value) {
793
+ if (reads.whole) {
794
+ return;
795
+ }
796
+ let values = reads.columns[column];
797
+ if (values === void 0) {
798
+ values = /* @__PURE__ */ new Set();
799
+ reads.columns[column] = values;
800
+ }
801
+ values.add(value);
802
+ }
803
+ var ReadLog = class {
804
+ constructor(skipped) {
805
+ this.skipped = skipped;
806
+ }
807
+ skipped;
808
+ /** Null for a relation the caller said to leave out. */
809
+ byRelation = /* @__PURE__ */ new Map();
810
+ lookedUp(relation, column, value) {
811
+ const reads = this.readsOf(relation);
812
+ if (reads !== null) {
813
+ noteIn(reads, column, value);
814
+ }
815
+ }
816
+ readWhole(relation) {
817
+ const reads = this.readsOf(relation);
818
+ if (reads !== null) {
819
+ reads.whole = true;
820
+ }
821
+ }
822
+ /** Note everything an inner log noted, for an answer cached inside another. */
823
+ absorb(inner) {
824
+ for (const [relation, reads] of inner.byRelation) {
825
+ if (reads === null) {
826
+ continue;
827
+ }
828
+ if (reads.whole) {
829
+ this.readWhole(relation);
830
+ continue;
831
+ }
832
+ reads.columns.forEach((values, column) => {
833
+ for (const value of values ?? []) {
834
+ this.lookedUp(relation, column, value);
835
+ }
836
+ });
837
+ }
838
+ }
839
+ /**
840
+ * The reads with what each one finds in `db` now. One question looks
841
+ * up the same values in many columns, so each value is kept once, and
842
+ * each column keeps a bit per value saying whether it was looked up
843
+ * there.
844
+ */
845
+ counted(db) {
846
+ const names = [];
847
+ const groups = [];
848
+ const columns = [];
849
+ const lookedUp = [];
850
+ const positions = /* @__PURE__ */ new Map();
851
+ const values = [];
852
+ const addGroup = (column, seen) => {
853
+ groups.push(names.length - 1);
854
+ columns.push(column);
855
+ lookedUp.push(seen);
856
+ for (const value of seen) {
857
+ if (!positions.has(value)) {
858
+ positions.set(value, values.length);
859
+ values.push(value);
860
+ }
861
+ }
862
+ };
863
+ for (const [relation, reads] of this.byRelation) {
864
+ if (reads === null) {
865
+ continue;
866
+ }
867
+ names.push(relation);
868
+ if (reads.whole) {
869
+ addGroup(WHOLE, NONE);
870
+ continue;
871
+ }
872
+ reads.columns.forEach((seen, column) => {
873
+ if (seen !== void 0) {
874
+ addGroup(column, seen);
875
+ }
876
+ });
877
+ }
878
+ const words = Math.ceil(values.length / 32);
879
+ const bits = new Uint32Array(groups.length * words);
880
+ let found = 0;
881
+ lookedUp.forEach((seen, group) => {
882
+ const relation = db.relationOf(names[groups[group]]);
883
+ const column = columns[group];
884
+ if (column === WHOLE) {
885
+ found += relation?.tuples.length ?? 0;
886
+ }
887
+ for (const value of seen) {
888
+ const at = positions.get(value);
889
+ bits[group * words + (at >>> 5)] |= 1 << (at & 31);
890
+ found += relation === void 0 ? 0 : bucketSize(relation, column, value);
891
+ }
892
+ });
893
+ return new ReadsNoted(
894
+ db,
895
+ this.skipped,
896
+ { names, groups, columns, values, bits },
897
+ found
898
+ );
899
+ }
900
+ /** @internal Null for a relation the caller said to leave out. */
901
+ readsOf(relation) {
902
+ let reads = this.byRelation.get(relation);
903
+ if (reads === void 0) {
904
+ reads = this.skipped.has(relation) ? null : { whole: false, columns: [] };
905
+ this.byRelation.set(relation, reads);
906
+ }
907
+ return reads;
908
+ }
909
+ };
910
+ var WHOLE = -1;
911
+ var NONE = /* @__PURE__ */ new Set();
912
+ var ReadsNoted = class {
913
+ constructor(db, skipped, lookups, found) {
914
+ this.skipped = skipped;
915
+ this.found = found;
916
+ this.names = lookups.names;
917
+ this.groups = lookups.groups;
918
+ this.columns = lookups.columns;
919
+ this.values = lookups.values;
920
+ this.bits = lookups.bits;
921
+ this.words = Math.ceil(lookups.values.length / 32);
922
+ this.relations = lookups.names.map((name) => db.relationOf(name));
923
+ this.removed = this.sumOver(db, removedFrom);
924
+ this.facts = this.sumOver(db, factsIn);
925
+ }
926
+ skipped;
927
+ found;
928
+ names;
929
+ groups;
930
+ columns;
931
+ values;
932
+ bits;
933
+ words;
934
+ removed;
935
+ /** The facts in every relation read, when the counts last matched. */
936
+ facts;
937
+ /** Each relation read, found by name the first time a check needs it. */
938
+ relations;
939
+ /**
940
+ * Whether every read would find what it found then. A fact left in a
941
+ * skipped relation would join a question asked now without anything
942
+ * here having looked it up, so one there makes the answer no. The
943
+ * counts are taken again only when a relation read has grown.
944
+ */
945
+ unchangedIn(db) {
946
+ if (!allEmpty(db, this.skipped)) {
947
+ return false;
948
+ }
949
+ if (this.sumOver(db, removedFrom) !== this.removed) {
950
+ return false;
951
+ }
952
+ const facts = this.sumOver(db, factsIn);
953
+ if (facts === this.facts) {
954
+ return true;
955
+ }
956
+ if (this.countIn(db, this.found) !== this.found) {
957
+ return false;
958
+ }
959
+ this.facts = facts;
960
+ return true;
961
+ }
962
+ relationAt(db, at) {
963
+ let relation = this.relations[at];
964
+ if (relation === void 0) {
965
+ relation = db.relationOf(this.names[at]);
966
+ this.relations[at] = relation;
967
+ }
968
+ return relation;
969
+ }
970
+ sumOver(db, measure) {
971
+ let sum = 0;
972
+ for (let at = 0; at < this.names.length; at++) {
973
+ const relation = this.relationAt(db, at);
974
+ sum += relation === void 0 ? 0 : measure(relation);
975
+ }
976
+ return sum;
977
+ }
978
+ /**
979
+ * The facts the reads find, stopping once past `limit`. With nothing
980
+ * retracted every count only grows, so passing the old total already
981
+ * says that one of them grew.
982
+ */
983
+ countIn(db, limit) {
984
+ let found = 0;
985
+ for (let group = 0; group < this.groups.length && found <= limit; group++) {
986
+ const relation = this.relationAt(db, this.groups[group]);
987
+ const column = this.columns[group];
988
+ if (column === WHOLE) {
989
+ found += relation?.tuples.length ?? 0;
990
+ } else if (relation !== void 0) {
991
+ found += this.countGroup(relation, group, column);
992
+ }
993
+ }
994
+ return found;
995
+ }
996
+ countGroup(relation, group, column) {
997
+ let found = 0;
998
+ const first = group * this.words;
999
+ for (let word = 0; word < this.words; word++) {
1000
+ let left = this.bits[first + word];
1001
+ while (left !== 0) {
1002
+ const bit = 31 - Math.clz32(left & -left);
1003
+ left &= left - 1;
1004
+ const value = this.values[word * 32 + bit];
1005
+ found += bucketSize(relation, column, value);
1006
+ }
1007
+ }
1008
+ return found;
1009
+ }
1010
+ };
1011
+ var removedFrom = (relation) => relation.removed;
1012
+ var factsIn = (relation) => relation.tuples.length;
1013
+ var skippedIn = /* @__PURE__ */ new WeakMap();
1014
+ function allEmpty(db, skipped) {
1015
+ let bySet = skippedIn.get(db);
1016
+ if (bySet === void 0) {
1017
+ bySet = /* @__PURE__ */ new WeakMap();
1018
+ skippedIn.set(db, bySet);
1019
+ }
1020
+ let known = bySet.get(skipped);
1021
+ if (known === void 0) {
1022
+ known = { found: [], missing: [...skipped] };
1023
+ bySet.set(skipped, known);
1024
+ }
1025
+ if (known.missing.length > 0) {
1026
+ const stillMissing = [];
1027
+ for (const name of known.missing) {
1028
+ const relation = db.relationOf(name);
1029
+ if (relation === void 0) {
1030
+ stillMissing.push(name);
1031
+ } else {
1032
+ known.found.push(relation);
1033
+ }
1034
+ }
1035
+ known.missing = stillMissing;
1036
+ }
1037
+ return known.found.every((relation) => relation.tuples.length === 0);
1038
+ }
1039
+ function noteReads(db, skipped, work) {
1040
+ const outer = db.readLog;
1041
+ const log = new ReadLog(skipped);
1042
+ const emptyAtStart = allEmpty(db, skipped);
1043
+ db.readLog = log;
1044
+ try {
1045
+ const value = work();
1046
+ return { value, reads: emptyAtStart ? log.counted(db) : null };
1047
+ } finally {
1048
+ db.readLog = outer;
1049
+ outer?.absorb(log);
1050
+ }
778
1051
  }
779
1052
 
780
1053
  // src/stratum.ts
@@ -800,7 +1073,7 @@ function planStratum(rules) {
800
1073
  }
801
1074
  return { rules, derived, readers, marked: new Uint8Array(rules.length) };
802
1075
  }
803
- var NONE = [];
1076
+ var NONE2 = [];
804
1077
  function rulesReading(stratum, seed, derivedOnly) {
805
1078
  const lists = [];
806
1079
  for (const [relation, tuples] of seed) {
@@ -816,7 +1089,7 @@ function rulesReading(stratum, seed, derivedOnly) {
816
1089
  }
817
1090
  }
818
1091
  if (lists.length === 0) {
819
- return NONE;
1092
+ return NONE2;
820
1093
  }
821
1094
  if (lists.length === 1) {
822
1095
  return lists[0];
@@ -986,6 +1259,8 @@ var ruleLabel = (r) => r.name ?? `${r.head.relation} :- ${r.body.map(literalName
986
1259
  var FEW_REMOVED = 16;
987
1260
  var Database = class {
988
1261
  store = /* @__PURE__ */ new Map();
1262
+ /** @internal Where reads are noted while `noteReads` runs. */
1263
+ readLog = null;
989
1264
  relation(name) {
990
1265
  const existing = this.store.get(name);
991
1266
  if (existing !== void 0) {
@@ -995,7 +1270,8 @@ var Database = class {
995
1270
  index: new FactIndex(),
996
1271
  tuples: [],
997
1272
  columns: [],
998
- combined: /* @__PURE__ */ new Map()
1273
+ combined: /* @__PURE__ */ new Map(),
1274
+ removed: 0
999
1275
  };
1000
1276
  this.store.set(name, created);
1001
1277
  return created;
@@ -1050,6 +1326,7 @@ var Database = class {
1050
1326
  }
1051
1327
  /** The tag stored for this fact, or undefined when there is none. */
1052
1328
  tagOf(relationName, tuple) {
1329
+ this.noteTupleRead(relationName, tuple);
1053
1330
  const relation = this.store.get(relationName);
1054
1331
  if (relation === void 0) {
1055
1332
  return void 0;
@@ -1058,9 +1335,20 @@ var Database = class {
1058
1335
  return key === void 0 ? void 0 : relation.index.tagOf(key);
1059
1336
  }
1060
1337
  has(relationName, tuple) {
1338
+ this.noteTupleRead(relationName, tuple);
1061
1339
  return this.store.get(relationName)?.index.find(tuple) !== void 0;
1062
1340
  }
1341
+ /** A fact matching the whole tuple agrees on its first column, so that lookup is enough to note. */
1342
+ noteTupleRead(relationName, tuple) {
1343
+ const first = tuple[0];
1344
+ if (first === void 0) {
1345
+ this.readLog?.readWhole(relationName);
1346
+ return;
1347
+ }
1348
+ this.readLog?.lookedUp(relationName, 0, first);
1349
+ }
1063
1350
  facts(relationName) {
1351
+ this.readLog?.readWhole(relationName);
1064
1352
  return this.store.get(relationName)?.tuples ?? [];
1065
1353
  }
1066
1354
  /**
@@ -1073,6 +1361,7 @@ var Database = class {
1073
1361
  * gets an index.
1074
1362
  */
1075
1363
  lookup(relationName, column, value) {
1364
+ this.readLog?.lookedUp(relationName, column, value);
1076
1365
  const relation = this.store.get(relationName);
1077
1366
  return relation === void 0 ? [] : bucketIn(relation, column, value);
1078
1367
  }
@@ -1108,6 +1397,7 @@ var Database = class {
1108
1397
  for (const key of going) {
1109
1398
  index.remove(key);
1110
1399
  }
1400
+ relation.removed += going.size;
1111
1401
  if (index.size === 0) {
1112
1402
  relation.tuples = [];
1113
1403
  index.clear();
@@ -1143,6 +1433,7 @@ var Database = class {
1143
1433
  return 0;
1144
1434
  }
1145
1435
  const removed = relation.tuples.length;
1436
+ relation.removed += removed;
1146
1437
  relation.tuples = [];
1147
1438
  relation.index.clear();
1148
1439
  relation.columns.length = 0;
@@ -1151,12 +1442,15 @@ var Database = class {
1151
1442
  return removed;
1152
1443
  }
1153
1444
  size(relationName) {
1154
- return this.store.get(relationName)?.tuples.length ?? 0;
1445
+ this.readLog?.readWhole(relationName);
1446
+ return sizeOf(this, relationName);
1155
1447
  }
1156
1448
  relationNames() {
1157
1449
  return [...this.store.keys()];
1158
1450
  }
1159
1451
  };
1452
+ var tuplesOf = (db, relation) => db.relationOf(relation)?.tuples ?? [];
1453
+ var sizeOf = (db, relation) => db.relationOf(relation)?.tuples.length ?? 0;
1160
1454
  function rulesDeriving(rules, relations) {
1161
1455
  const wanted = new Set(relations);
1162
1456
  const pending = [...relations];
@@ -1352,6 +1646,7 @@ function evaluateRule(db, deltas, r, deltaIndex, budget) {
1352
1646
  }
1353
1647
  const byValue = newFactsByColumn(rows, start.column, budget);
1354
1648
  const first = body[start.index];
1649
+ noteSharedLookups(db, first, start, rows);
1355
1650
  for (const tuple of start.source) {
1356
1651
  budget.examined++;
1357
1652
  if (budget.examined > budget.limit) {
@@ -1383,7 +1678,7 @@ function smallerStart(db, body, deltaAt, rows) {
1383
1678
  if (column === -1) {
1384
1679
  continue;
1385
1680
  }
1386
- const source = narrowedSource(db, literal, null) ?? db.facts(literal.relation);
1681
+ const source = narrowedSource(db, literal, null) ?? tuplesOf(db, literal.relation);
1387
1682
  const worthIt = indexed?.has(column) ? source.length < rows.length : source.length * 2 < rows.length;
1388
1683
  if (worthIt && (best === null || source.length < best.source.length)) {
1389
1684
  best = {
@@ -1441,6 +1736,7 @@ function nextLiteral(db, body, taken, bindings) {
1441
1736
  continue;
1442
1737
  }
1443
1738
  if (source.length === 0) {
1739
+ noteLookup(db, literal, bindings);
1444
1740
  return -1;
1445
1741
  }
1446
1742
  if (narrowest === null || source.length < narrowest.length) {
@@ -1449,6 +1745,7 @@ function nextLiteral(db, body, taken, bindings) {
1449
1745
  }
1450
1746
  }
1451
1747
  if (narrowest !== null) {
1748
+ noteLookup(db, body[pick], bindings);
1452
1749
  nextSource = narrowedFurther(db, body[pick], bindings, narrowest);
1453
1750
  return nextSource.length === 0 ? -1 : pick;
1454
1751
  }
@@ -1460,6 +1757,59 @@ function nextLiteral(db, body, taken, bindings) {
1460
1757
  }
1461
1758
  return body.findIndex((_, index) => !(taken & 1 << index));
1462
1759
  }
1760
+ function noteLookup(db, literal, bindings) {
1761
+ const reads = db.readLog?.readsOf(literal.relation) ?? null;
1762
+ if (reads === null) {
1763
+ return;
1764
+ }
1765
+ for (let column2 = 0; column2 < literal.terms.length; column2++) {
1766
+ const term2 = literal.terms[column2];
1767
+ if (term2.type === "variable") {
1768
+ const value = boundValue(bindings, term2.name);
1769
+ if (value !== void 0) {
1770
+ noteIn(reads, column2, value);
1771
+ return;
1772
+ }
1773
+ }
1774
+ }
1775
+ const column = fewestFactsUnderConstant(db, literal);
1776
+ const term = literal.terms[column];
1777
+ if (term?.type === "constant") {
1778
+ noteIn(reads, column, term.value);
1779
+ }
1780
+ }
1781
+ function fewestFactsUnderConstant(db, literal) {
1782
+ const relation = db.relationOf(literal.relation);
1783
+ let best = -1;
1784
+ let bestFacts = Number.POSITIVE_INFINITY;
1785
+ for (let column = 0; column < literal.terms.length; column++) {
1786
+ const term = literal.terms[column];
1787
+ if (term.type !== "constant") {
1788
+ continue;
1789
+ }
1790
+ const facts = relation === void 0 ? 0 : bucketSize(relation, column, term.value);
1791
+ if (facts < bestFacts) {
1792
+ best = column;
1793
+ bestFacts = facts;
1794
+ }
1795
+ }
1796
+ return best;
1797
+ }
1798
+ function noteSharedLookups(db, first, start, rows) {
1799
+ const reads = db.readLog?.readsOf(first.relation) ?? null;
1800
+ if (reads === null) {
1801
+ return;
1802
+ }
1803
+ const column = first.terms.findIndex(
1804
+ (term) => term.type === "variable" && term.name === start.variable
1805
+ );
1806
+ for (const row of rows) {
1807
+ const value = row[start.column];
1808
+ if (value !== void 0) {
1809
+ noteIn(reads, column, value);
1810
+ }
1811
+ }
1812
+ }
1463
1813
  function narrowedSource(db, literal, bindings) {
1464
1814
  const relation = db.relationOf(literal.relation);
1465
1815
  let narrowest = null;
@@ -1511,7 +1861,11 @@ function constantColumnsOf(literal) {
1511
1861
  values.push(term.value);
1512
1862
  }
1513
1863
  });
1514
- known = columns.length === 0 ? null : { columns, values };
1864
+ known = columns.length === 0 ? null : {
1865
+ columns,
1866
+ values,
1867
+ bucketKey: deltaBucketKey(literal.relation, columns[0])
1868
+ };
1515
1869
  constantColumns.set(literal, known);
1516
1870
  }
1517
1871
  return known;
@@ -1524,7 +1878,7 @@ function hasNoNewRows(deltas, literal) {
1524
1878
  if (rows.length === 0 || constants === null) {
1525
1879
  return rows.length === 0;
1526
1880
  }
1527
- const buckets = deltaBuckets.get(deltas)?.get(deltaBucketKey(literal.relation, constants.columns[0]));
1881
+ const buckets = deltaBuckets.get(deltas)?.get(constants.bucketKey);
1528
1882
  return buckets !== void 0 && !buckets.has(constants.values[0]);
1529
1883
  }
1530
1884
  function deltaRows(deltas, literal, budget) {
@@ -1539,7 +1893,7 @@ function deltaRows(deltas, literal, budget) {
1539
1893
  deltaBuckets.set(deltas, byColumn);
1540
1894
  }
1541
1895
  const column = constants.columns[0];
1542
- const key = deltaBucketKey(literal.relation, column);
1896
+ const key = constants.bucketKey;
1543
1897
  let buckets = byColumn.get(key);
1544
1898
  if (buckets === void 0) {
1545
1899
  buckets = /* @__PURE__ */ new Map();
@@ -1684,7 +2038,7 @@ function canResume(rules, state) {
1684
2038
  function factsSince(db, marks) {
1685
2039
  const added = /* @__PURE__ */ new Map();
1686
2040
  for (const name of db.relationNames()) {
1687
- const all = db.facts(name);
2041
+ const all = tuplesOf(db, name);
1688
2042
  const mark = marks.get(name) ?? 0;
1689
2043
  if (all.length > mark) {
1690
2044
  added.set(name, all.slice(mark));
@@ -1695,7 +2049,7 @@ function factsSince(db, marks) {
1695
2049
  function currentMarks(db) {
1696
2050
  const marks = /* @__PURE__ */ new Map();
1697
2051
  for (const name of db.relationNames()) {
1698
- marks.set(name, db.size(name));
2052
+ marks.set(name, sizeOf(db, name));
1699
2053
  }
1700
2054
  return marks;
1701
2055
  }
@@ -1757,9 +2111,14 @@ function shapeOf(rules) {
1757
2111
  return shape;
1758
2112
  }
1759
2113
  function couldProduce(db, rule2) {
1760
- return rule2.body.every(
1761
- (literal) => literal.negated || db.size(literal.relation) > 0
2114
+ const empty = rule2.body.find(
2115
+ (literal) => !literal.negated && sizeOf(db, literal.relation) === 0
1762
2116
  );
2117
+ if (empty === void 0) {
2118
+ return true;
2119
+ }
2120
+ db.readLog?.readWhole(empty.relation);
2121
+ return false;
1763
2122
  }
1764
2123
  function storedMergeFor(algebra) {
1765
2124
  return (stored, incoming) => {
@@ -1808,6 +2167,17 @@ function runRules(db, rules, algebra, given) {
1808
2167
  };
1809
2168
  const mergeStored = algebra === void 0 ? void 0 : storedMergeFor(algebra);
1810
2169
  const marks = canResume(rules, state) ? state.marks : void 0;
2170
+ const producible = /* @__PURE__ */ new Set();
2171
+ const canProduce = (r) => {
2172
+ if (producible.has(r)) {
2173
+ return true;
2174
+ }
2175
+ if (!couldProduce(db, r)) {
2176
+ return false;
2177
+ }
2178
+ producible.add(r);
2179
+ return true;
2180
+ };
1811
2181
  const runStratum = (stratum) => {
1812
2182
  let delta = /* @__PURE__ */ new Map();
1813
2183
  const record = (relation, tuple) => {
@@ -1850,7 +2220,7 @@ function runRules(db, rules, algebra, given) {
1850
2220
  return;
1851
2221
  }
1852
2222
  const startedAt2 = performance.now();
1853
- const before = db.size(r.head.relation);
2223
+ const before = sizeOf(db, r.head.relation);
1854
2224
  const readBefore = budget.examined;
1855
2225
  deriveInto(r, seed, deltaIndex);
1856
2226
  chargeRule(
@@ -1858,14 +2228,14 @@ function runRules(db, rules, algebra, given) {
1858
2228
  r.head.relation,
1859
2229
  r.body.map(literalName),
1860
2230
  performance.now() - startedAt2,
1861
- db.size(r.head.relation) - before,
2231
+ sizeOf(db, r.head.relation) - before,
1862
2232
  budget.examined - readBefore
1863
2233
  );
1864
2234
  };
1865
2235
  const applyDelta = (seed, derivedOnly) => {
1866
2236
  for (const at of rulesReading(stratum, seed, derivedOnly)) {
1867
2237
  const r = stratum.rules[at];
1868
- if (!couldProduce(db, r)) {
2238
+ if (!canProduce(r)) {
1869
2239
  continue;
1870
2240
  }
1871
2241
  const positives = positivesOf(r);
@@ -1883,13 +2253,13 @@ function runRules(db, rules, algebra, given) {
1883
2253
  };
1884
2254
  if (marks === void 0 || marks === null) {
1885
2255
  for (const r of stratum.rules) {
1886
- if (!couldProduce(db, r)) {
2256
+ if (!canProduce(r)) {
1887
2257
  continue;
1888
2258
  }
1889
2259
  const all = /* @__PURE__ */ new Map();
1890
2260
  for (const l of r.body) {
1891
2261
  if (!l.negated) {
1892
- all.set(l.relation, db.facts(l.relation));
2262
+ all.set(l.relation, tuplesOf(db, l.relation));
1893
2263
  }
1894
2264
  }
1895
2265
  runOneRule(r, all, -1);
@@ -1926,7 +2296,7 @@ function runRules(db, rules, algebra, given) {
1926
2296
  state.marks = currentMarks(db);
1927
2297
  if (isProfiling()) {
1928
2298
  chargeRelationSizes(
1929
- db.relationNames().map((name) => [name, db.size(name)]),
2299
+ db.relationNames().map((name) => [name, sizeOf(db, name)]),
1930
2300
  derivedRelations
1931
2301
  );
1932
2302
  }
@@ -1963,6 +2333,7 @@ function addTo(buckets, relation, tuple) {
1963
2333
  formatProfile,
1964
2334
  lit,
1965
2335
  notLit,
2336
+ noteReads,
1966
2337
  profileEvaluation,
1967
2338
  profileEvaluationAsync,
1968
2339
  proofOf,