diff --git a/.cspell.json b/.cspell.json index a9926c364..96b8cde14 100644 --- a/.cspell.json +++ b/.cspell.json @@ -18,7 +18,8 @@ "esbuild", "coloneqq", "FiltVect", - "networkidle" + "networkidle", + "devlog" ], "words": [ "abelian", diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 020e30eb0..61e6b5934 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -34,9 +34,6 @@ jobs: pnpm db:setup:catdat pnpm db:update - - name: Redundancy check - run: pnpm db:redundancies - - name: Cache Playwright browsers uses: actions/cache@v6 with: diff --git a/databases/app/scripts/setup.ts b/databases/app/scripts/setup.ts index 106d38bde..7bcc51691 100644 --- a/databases/app/scripts/setup.ts +++ b/databases/app/scripts/setup.ts @@ -1,5 +1,6 @@ import dotenv from 'dotenv' import { createClient } from '@libsql/client' +import { devlog } from '$shared/utils' dotenv.config({ quiet: true }) @@ -39,7 +40,7 @@ async function create_visits_table() { ) `) - console.info('Visits table ready') + devlog('Visits table ready') } /** @@ -58,7 +59,7 @@ async function create_submissions_table() { ) `) - console.info('Submissions table ready') + devlog('Submissions table ready') } /** @@ -74,5 +75,5 @@ async function create_actions_table() { ) `) - console.info('User actions table ready') + devlog('User actions table ready') } diff --git a/databases/catdat/scripts/config.ts b/databases/catdat/scripts/config.ts deleted file mode 100644 index 7014eef2d..000000000 --- a/databases/catdat/scripts/config.ts +++ /dev/null @@ -1,17 +0,0 @@ -export const STRUCTURE_TYPES = ['category', 'functor', 'morphism'] as const - -export type StructureType = (typeof STRUCTURE_TYPES)[number] - -export const STRUCTURE_TYPES_WITH_DUALS: StructureType[] = ['category'] - -export const PLURALS = { - category: 'categories', - functor: 'functors', - morphism: 'morphisms' -} as const - -export const TABLES = { - category: 'categories', - functor: 'functors', - morphism: 'morphisms' -} as const diff --git a/databases/catdat/scripts/deduce-implications.ts b/databases/catdat/scripts/deduce-implications.ts index ef7eff7ce..14ed69ea8 100644 --- a/databases/catdat/scripts/deduce-implications.ts +++ b/databases/catdat/scripts/deduce-implications.ts @@ -1,8 +1,13 @@ -import { STRUCTURE_TYPES_WITH_DUALS, type StructureType } from './config' -import { are_equal_sets, parse_nested_json_set, parse_json_set } from './utils/helpers' -import { get_client } from './utils/db' +import { type StructureType, STRUCTURE_TYPES_WITH_DUALS } from '$shared/config' +import { + are_equal_sets, + parse_nested_json_set, + parse_json_set, + devlog +} from '$shared/utils' +import { get_client } from '$shared/db' -const db = get_client() +const db = get_client({ readonly: false }) /** * Clears all deduced implications. This is done before the deduction starts. @@ -151,7 +156,7 @@ export function create_dualized_implications(type: StructureType) { } } - console.info(`Deduced ${count} ${type} implications by duality`) + devlog(`Deduced ${count} ${type} implications by duality`) }) insert_duals() @@ -203,5 +208,5 @@ export function create_self_dual_implications(type: StructureType) { conclusion_insert.run(implication_id, p.dual, type) } - console.info(`Deduced ${relevant_props.length} ${type} implications by self-duality`) + devlog(`Deduced ${relevant_props.length} ${type} implications by self-duality`) } diff --git a/databases/catdat/scripts/deduce-special-morphisms.ts b/databases/catdat/scripts/deduce-special-morphisms.ts index b3bcf14e8..5d167fb82 100644 --- a/databases/catdat/scripts/deduce-special-morphisms.ts +++ b/databases/catdat/scripts/deduce-special-morphisms.ts @@ -1,6 +1,7 @@ -import { get_client } from './utils/db' +import { get_client } from '$shared/db' +import { devlog } from '$shared/utils' -const db = get_client() +const db = get_client({ readonly: false }) // TODO: deduce further morphisms, // e.g. isomorphisms = bijective morphisms in algebraic categories, @@ -46,5 +47,5 @@ function deduce_special_morphisms_of_dual_categories() { ) .run() - console.info(`Deduced ${res.changes} special morphisms by duality`) + devlog(`Deduced ${res.changes} special morphisms by duality`) } diff --git a/databases/catdat/scripts/deduce-special-objects.ts b/databases/catdat/scripts/deduce-special-objects.ts index c944a3b49..c65c57914 100644 --- a/databases/catdat/scripts/deduce-special-objects.ts +++ b/databases/catdat/scripts/deduce-special-objects.ts @@ -1,6 +1,7 @@ -import { get_client } from './utils/db' +import { get_client } from '$shared/db' +import { devlog } from '$shared/utils' -const db = get_client() +const db = get_client({ readonly: false }) export function deduce_special_objects() { console.info('\n--- Deduce special objects ---') @@ -40,5 +41,5 @@ function deduce_special_objects_of_dual_categories() { ) .run() - console.info(`Deduced ${res.changes} special objects by duality`) + devlog(`Deduced ${res.changes} special objects by duality`) } diff --git a/databases/catdat/scripts/deduce-structure-properties.ts b/databases/catdat/scripts/deduce-structure-properties.ts index cbb827ba3..a17f331b5 100644 --- a/databases/catdat/scripts/deduce-structure-properties.ts +++ b/databases/catdat/scripts/deduce-structure-properties.ts @@ -1,168 +1,99 @@ /** * This file contains all functions that are used to deduce properties - * for categories and functors. + * for categories and other categorical structures. */ -import { type Database, SqliteError } from 'better-sqlite3' -import { is_subset } from './utils/helpers' -import { get_client } from './utils/db' +import { type Database } from 'better-sqlite3' +import { deduce_properties, refute_properties } from '$shared/deduction.utils' +import { get_client } from '$shared/db' import { get_properties_dict, get_property_assignments, type PropertyMeta } from './utils/properties' -import { - get_contradiction_string, - get_normalized_implications, - get_proof_string, - NormalizedImplication -} from './utils/implications' -import { STRUCTURE_TYPES_WITH_DUALS, type StructureType } from './config' +import { get_contradiction_string, get_proof_string } from './utils/implications' +import { type StructureType, STRUCTURE_TYPES_WITH_DUALS } from '$shared/config' import { get_structures, is_dual_structure, type StructureMeta } from './utils/structures' +import { get_normalized_implications, NormalizedImplication } from '$shared/implications' +import { devlog } from '$shared/utils' /** - * Returns the set of satisfied properties that can be deduced from a set - * of satisfied properties, based on a list of normalized implications. If a - * property dictionary is provided, human-readable proofs are generated as well. + * Deduce satisfied properties for a given structure from given ones + * by using a list of normalized implications. + * Human-readable proofs are generated as well. + * Warning: This mutates the set of satisfied properties. */ -export function get_deduced_satisfied_properties( - satisfied_properties: Set, +function deduce_satisfied_properties( + db: Database, + structure: StructureMeta, implications: NormalizedImplication[], - options: { - properties_dict?: Record - stop_when_found?: string - }, - type: StructureType, - associated_satisfied_properties?: Record> + satisfied_properties: Set, + unsatisfied_properties: Set, + properties_dict: Record, + type: StructureType ) { - const found = new Set() - const proofs: Record = {} - const deduced_satisfied_properties = new Set(satisfied_properties) - - while (true) { - const newly_found = new Set() - - for (const implication of implications) { - const is_valid = - is_subset(implication.assumptions, deduced_satisfied_properties) && - !deduced_satisfied_properties.has(implication.conclusion) && - !newly_found.has(implication.conclusion) - - if (!is_valid) continue - - if (implication.mapped_assumptions) { - const is_applicable = Object.keys(implication.mapped_assumptions).every( - (key) => { - return is_subset( - implication.mapped_assumptions?.[key] ?? new Set(), - associated_satisfied_properties?.[key] ?? new Set() - ) - } - ) - if (!is_applicable) continue - } - - newly_found.add(implication.conclusion) - found.add(implication.conclusion) - - if (options?.stop_when_found === implication.conclusion) { - deduced_satisfied_properties.add(implication.conclusion) - return { deduced_satisfied_properties, found, proofs } - } + const { found, proofs, stop_property } = deduce_properties( + satisfied_properties, + implications, + (implication) => ({ + proof: get_proof_string(implication, properties_dict, type), + stop: unsatisfied_properties.has(implication.conclusion) + }), + structure.associated_satisfied_properties + ) - if (options.properties_dict) { - proofs[implication.conclusion] = get_proof_string( - implication, - options.properties_dict, - type - ) - } - } + if (stop_property) { + console.error( + `❌ Property assignments of ${structure.id} are contradictory: "${stop_property}" can be deduced from its satisfied properties, but is marked as unsatisfied.` + ) + process.exit(1) + } - for (const p of newly_found) deduced_satisfied_properties.add(p) + for (const p of found) satisfied_properties.add(p) - if (!newly_found.size) break - } + save_satisfied_properties(db, structure.id, found, proofs, type) - return { deduced_satisfied_properties, found, proofs } + devlog(`Deduced ${found.size} satisfied properties for ${structure.id}`) } /** - * Returns the set of unsatisfied properties that can be deduced from - * a set of satisfied properties and a set of unsatisfied properties, - * based on a list of normalized implications. If a property dictionary - * is provided, human-readable proofs are generated as well. + * Deduce unsatisfied properties for a given structure from given ones + * by using satisfied properties and a list of normalized implications. + * Human-readable proofs are generated as well. + * Warning: This mutates the set of unsatisfied properties. */ -export function get_deduced_unsatisfied_properties( +function deduce_unsatisfied_properties( + db: Database, + structure: StructureMeta, + implications: NormalizedImplication[], satisfied_properties: Set, unsatisfied_properties: Set, - implications: NormalizedImplication[], - options: { - properties_dict?: Record - stop_when_found?: string - }, - type: StructureType, - associated_satisfied_properties?: Record> + properties_dict: Record, + type: StructureType ) { - const found = new Set() - const proofs: Record = {} - const deduced_unsatisfied_properties = new Set(unsatisfied_properties) - - while (true) { - const newly_found = new Set() - - for (const implication of implications) { - if (!deduced_unsatisfied_properties.has(implication.conclusion)) continue - for (const p of implication.assumptions) { - const is_valid = - !deduced_unsatisfied_properties.has(p) && - !newly_found.has(p) && - is_subset(implication.assumptions, satisfied_properties, { - exception: p - }) - if (!is_valid) continue - - if (implication.mapped_assumptions) { - const is_applicable = Object.keys( - implication.mapped_assumptions - ).every((key) => { - return is_subset( - implication.mapped_assumptions?.[key] ?? new Set(), - associated_satisfied_properties?.[key] ?? new Set() - ) - }) - if (!is_applicable) continue - } - - if (satisfied_properties.has(p)) { - throw new Error(`Contradiction has been found for: ${p}`) - } - - if (options?.stop_when_found === p) { - deduced_unsatisfied_properties.add(p) - return { deduced_unsatisfied_properties, found, proofs } - } - - newly_found.add(p) - found.add(p) - - if (options.properties_dict) { - proofs[p] = get_contradiction_string( - implication, - options.properties_dict, - p, - type - ) - } + const { found, proofs } = refute_properties( + satisfied_properties, + unsatisfied_properties, + implications, + (implication, property) => { + return { + proof: get_contradiction_string( + implication, + properties_dict, + property, + type + ), + stop: false } - } + }, + structure.associated_satisfied_properties + ) - for (const p of newly_found) deduced_unsatisfied_properties.add(p) + for (const p of found) unsatisfied_properties.add(p) - if (!newly_found.size) break - } + save_unsatisfied_properties(db, structure.id, found, proofs, type) - return { deduced_unsatisfied_properties, found, proofs } + devlog(`Deduced ${found.size} unsatisfied properties for ${structure.id}`) } /** @@ -172,33 +103,19 @@ function save_satisfied_properties( db: Database, structure_id: string, found: Set, - proofs: Record, + proofs: Partial>, type: StructureType ) { if (found.size === 0) return - const err_msg = `❌ Failed to complete deduction of satisfied properties for ${structure_id} because of a conflict. The likely cause is a contradiction between its assigned properties.` - const property_insert = db.prepare(` INSERT INTO property_assignments (structure_id, property_id, type, is_satisfied, proof, is_deduced) VALUES (?, ?, ?, TRUE, ?, TRUE) `) - try { - for (const p of found) { - property_insert.run(structure_id, p, type, proofs[p]) - } - } catch (err) { - if (err instanceof SqliteError) { - if (err.code.startsWith('SQLITE_CONSTRAINT')) { - console.error(err_msg) - } - console.error(err.message) - } else { - console.error(err) - } - process.exit(1) + for (const p of found) { + property_insert.run(structure_id, p, type, proofs[p]) } } @@ -209,94 +126,22 @@ function save_unsatisfied_properties( db: Database, structure_id: string, found: Set, - proofs: Record, + proofs: Partial>, type: StructureType ) { if (found.size === 0) return - const err_msg = `❌ Failed to complete deduction of unsatisfied properties for ${structure_id} because of a conflict. The likely cause is a contradiction between its assigned properties.` - const property_insert = db.prepare(` INSERT INTO property_assignments (structure_id, property_id, type, is_satisfied, proof, is_deduced) VALUES (?, ?, ?, FALSE, ?, TRUE) `) - try { - for (const p of found) { - property_insert.run(structure_id, p, type, proofs[p]) - } - } catch (err) { - if (err instanceof SqliteError) { - if (err.code.startsWith('SQLITE_CONSTRAINT')) { - console.error(err_msg) - } - console.error(err.message) - } else { - console.error(err) - } - process.exit(1) + for (const p of found) { + property_insert.run(structure_id, p, type, proofs[p]) } } -/** - * Deduce satisfied properties for a given structure from given ones - * by using the list of normalized implications. - * Warning: This mutates the set of satisfied properties. - */ -function deduce_satisfied_properties( - db: Database, - structure: StructureMeta, - implications: NormalizedImplication[], - satisfied_properties: Set, - properties_dict: Record, - type: StructureType -) { - const { found, proofs } = get_deduced_satisfied_properties( - satisfied_properties, - implications, - { properties_dict }, - type, - structure.associated_satisfied_properties - ) - - for (const p of found) satisfied_properties.add(p) - - save_satisfied_properties(db, structure.id, found, proofs, type) - - console.info(`Deduced ${found.size} satisfied properties for ${structure.id}`) -} - -/** - * Deduce unsatisfied properties for a given structure from given ones - * by using the satisfied properties and the list of normalized implications. - * Warning: This mutates the set of unsatisfied properties. - */ -function deduce_unsatisfied_properties( - db: Database, - structure: StructureMeta, - implications: NormalizedImplication[], - satisfied_properties: Set, - unsatisfied_properties: Set, - properties_dict: Record, - type: StructureType -) { - const { found, proofs } = get_deduced_unsatisfied_properties( - satisfied_properties, - unsatisfied_properties, - implications, - { properties_dict }, - type, - structure.associated_satisfied_properties - ) - - for (const p of found) unsatisfied_properties.add(p) - - save_unsatisfied_properties(db, structure.id, found, proofs, type) - - console.info(`Deduced ${found.size} unsatisfied properties for ${structure.id}`) -} - /** * Assign dual properties to dual structures: * If C has property P, then C^op has property P^op (if defined). @@ -355,7 +200,7 @@ function deduce_dual_properties( property_insert.run(structure.id, p, type, 1, proof_satisfied) } - console.info( + devlog( `Deduced ${new_satisfied.size} satisfied properties by duality for ${structure.id}` ) @@ -363,7 +208,7 @@ function deduce_dual_properties( property_insert.run(structure.id, q, type, 0, proof_unsatisfied) } - console.info( + devlog( `Deduced ${new_unsatisfied.size} unsatisfied properties by duality for ${structure.id}` ) @@ -371,7 +216,7 @@ function deduce_dual_properties( property_insert.run(structure.id, q, type, null, proof_undecidable) } - console.info( + devlog( `Deduced ${new_undecidable.size} undecidable properties by duality for ${structure.id}` ) } @@ -392,7 +237,7 @@ function delete_deduced_properties(db: Database, type: StructureType) { export function deduce_properties_for_structures(type: StructureType) { console.info(`\n--- Deduce ${type} properties ---`) - const db = get_client() + const db = get_client({ readonly: false }) delete_deduced_properties(db, type) @@ -410,6 +255,7 @@ export function deduce_properties_for_structures(type: StructureType) { structure, implications, assigned.satisfied, + assigned.unsatisfied, properties_dict, type ) @@ -455,6 +301,7 @@ export function deduce_properties_for_structures(type: StructureType) { structure, implications, assigned.satisfied, + assigned.unsatisfied, properties_dict, type ) diff --git a/databases/catdat/scripts/proof-length.ts b/databases/catdat/scripts/proof-length.ts index af5544169..ebfde75c5 100644 --- a/databases/catdat/scripts/proof-length.ts +++ b/databases/catdat/scripts/proof-length.ts @@ -1,7 +1,8 @@ -import { type StructureType } from './config' -import { get_client } from './utils/db' +import { STRUCTURE_TYPES, type StructureType } from '$shared/config' +import { get_client } from '$shared/db' +get_client -const db = get_client() +const db = get_client({ readonly: true }) const PROOF_LENGTH_THRESHOLD = 1200 @@ -12,10 +13,12 @@ report_long_proofs() * perhaps be moved to a separate content page. */ function report_long_proofs() { - report_long_property_proofs('category') - report_long_property_proofs('functor') - report_long_implication_proofs('category') - report_long_implication_proofs('functor') + for (const type of STRUCTURE_TYPES) { + report_long_property_proofs(type) + } + for (const type of STRUCTURE_TYPES) { + report_long_implication_proofs(type) + } } function report_long_property_proofs(type: StructureType) { diff --git a/databases/catdat/scripts/redundancies.ts b/databases/catdat/scripts/redundancies.ts index 3e1af4df6..bf3520a7e 100644 --- a/databases/catdat/scripts/redundancies.ts +++ b/databases/catdat/scripts/redundancies.ts @@ -1,17 +1,15 @@ -import { get_client } from './utils/db' -import { - get_deduced_satisfied_properties, - get_deduced_unsatisfied_properties -} from './deduce-structure-properties' +import { get_client } from '$shared/db' import { get_property_assignments_by_deduction } from './utils/properties' -import type { StructureType } from './config' +import { STRUCTURE_TYPES, type StructureType } from '$shared/config' import { get_normalized_implications, + stringify_implication, type NormalizedImplication -} from './utils/implications' +} from '$shared/implications' +import { deduce_properties, refute_properties } from '$shared/deduction.utils' import { get_structures } from './utils/structures' -const db = get_client() +const db = get_client({ readonly: true }) check_redundancies() @@ -20,13 +18,14 @@ check_redundancies() * No error is thrown intentionally. */ function check_redundancies() { - check_redundant_property_assignments('category') - check_redundant_property_assignments('functor') + for (const type of STRUCTURE_TYPES) { + check_redundant_property_assignments(type) + } } /** * Checks for redundant (structure, property)-assignments and logs - * one per functor if any are found (satisfied or unsatisfied). + * one per structure if any are found (satisfied or unsatisfied). * No error is thrown intentionally. */ function check_redundant_property_assignments(type: StructureType) { @@ -48,7 +47,6 @@ function check_redundant_property_assignments(type: StructureType) { for (const structure of structures) { const redundant_satisfied_property = get_redundant_satisfied_property( - type, assignments[structure.id].satisfied.non_deduced, implications, ignore_dict[structure.id], @@ -69,7 +67,6 @@ function check_redundant_property_assignments(type: StructureType) { ]) const redundant_unsatisfied_property = get_redundant_unsatisfied_property( - type, all_satisfied_properties, assignments[structure.id].unsatisfied.non_deduced, implications, @@ -104,25 +101,31 @@ function check_redundant_property_assignments(type: StructureType) { * If no such property exists, null is returned. */ function get_redundant_satisfied_property( - type: StructureType, satisfied_properties: Set, implications: NormalizedImplication[], ignored: Set = new Set(), - associated_satisfied_properties?: Record> + associated_satisfied_properties?: Partial>> ) { for (const p of [...satisfied_properties]) { if (ignored.has(p)) continue + satisfied_properties.delete(p) - const { deduced_satisfied_properties } = get_deduced_satisfied_properties( + + const { deduced_satisfied_properties } = deduce_properties( satisfied_properties, implications, - { stop_when_found: p }, - type, + (implication) => ({ + proof: stringify_implication(implication), + stop: implication.conclusion === p + }), associated_satisfied_properties ) + if (deduced_satisfied_properties.has(p)) return p + satisfied_properties.add(p) } + return null } @@ -133,25 +136,30 @@ function get_redundant_satisfied_property( * If no such property exists, null is returned. */ function get_redundant_unsatisfied_property( - type: StructureType, satisfied_properties: Set, unsatisfied_properties: Set, implications: NormalizedImplication[], ignored: Set = new Set(), - associated_satisfied_properties?: Record> + associated_satisfied_properties?: Partial>> ) { for (const p of [...unsatisfied_properties]) { if (ignored.has(p)) continue + unsatisfied_properties.delete(p) - const { deduced_unsatisfied_properties } = get_deduced_unsatisfied_properties( + + const { deduced_unsatisfied_properties } = refute_properties( satisfied_properties, unsatisfied_properties, implications, - { stop_when_found: p }, - type, + (implication, property) => ({ + proof: stringify_implication(implication), + stop: property === p + }), associated_satisfied_properties ) + if (deduced_unsatisfied_properties.has(p)) return p + unsatisfied_properties.add(p) } return null diff --git a/databases/catdat/scripts/restrict-functor-properties.ts b/databases/catdat/scripts/restrict-functor-properties.ts index c7af74cb2..70588dd65 100644 --- a/databases/catdat/scripts/restrict-functor-properties.ts +++ b/databases/catdat/scripts/restrict-functor-properties.ts @@ -1,6 +1,7 @@ -import { get_client } from './utils/db' +import { get_client } from '$shared/db' +import { devlog } from '$shared/utils' -const db = get_client() +const db = get_client({ readonly: false }) /** * Ensures that only functors with target Set are representable. @@ -38,5 +39,5 @@ export function restrict_representable_functors() { ) .run() - console.info(`Deduced that ${res.changes} functors cannot be representable`) + devlog(`Deduced that ${res.changes} functors cannot be representable`) } diff --git a/databases/catdat/scripts/seed.ts b/databases/catdat/scripts/seed.ts index 78eb7bfd1..9c60ddf84 100644 --- a/databases/catdat/scripts/seed.ts +++ b/databases/catdat/scripts/seed.ts @@ -1,6 +1,6 @@ import path from 'node:path' import { seed_file, seed_files } from './utils/seed.helpers' -import { get_client } from './utils/db' +import { get_client } from '$shared/db' import type { CategoryYaml, ConfigYaml, @@ -12,9 +12,10 @@ import type { MorphismYaml } from './utils/seed.types' import { create_schema_hash, get_saved_schema_hash } from './utils/schema' -import { PLURALS, STRUCTURE_TYPES, type StructureType } from './config' +import { STRUCTURE_TYPES, type StructureType, PLURALS } from '$shared/config' +import { are_disjoint, devlog } from '$shared/utils' -const db = get_client() +const db = get_client({ readonly: false }) const data_folder = path.resolve('databases', 'catdat', 'data') @@ -47,7 +48,7 @@ function seed() { * Checks if the schema is up-to-date, and throws an error otherwise. */ function check_schema() { - console.info(`\nCheck schema ...`) + devlog(`\nCheck schema ...`) const schema_hash = get_saved_schema_hash() const actual_hash = create_schema_hash() @@ -62,7 +63,7 @@ function check_schema() { * Clears all tables in the database. This is done as a first step. */ function clear_all_tables() { - console.info(`\nClear all tables ...`) + devlog(`\nClear all tables ...`) const tx = db.transaction(() => { db.pragma('defer_foreign_keys = ON') @@ -89,7 +90,6 @@ function clear_all_tables() { db.prepare(`DELETE FROM structure_tags`).run() db.prepare(`DELETE FROM relations`).run() - // this deletes categories and functors automatically db.prepare(`DELETE FROM structures`).run() }) @@ -216,6 +216,20 @@ function seed_structures({ } function insert_structure(structure: T) { + const properties_are_disjoint = are_disjoint( + [ + structure.satisfied_properties, + structure.unsatisfied_properties, + structure.undecidable_properties ?? [] + ], + (entry) => entry.property + ) + + if (!properties_are_disjoint) { + console.error(`❌ Properties of ${structure.id} are contradictory.`) + process.exit(1) + } + structure_insert.run( structure.id, type, diff --git a/databases/catdat/scripts/setup.ts b/databases/catdat/scripts/setup.ts index f70c93a2d..4c1fa136e 100644 --- a/databases/catdat/scripts/setup.ts +++ b/databases/catdat/scripts/setup.ts @@ -1,7 +1,8 @@ import fs from 'node:fs' import path from 'node:path' -import { get_client } from './utils/db' +import { get_client } from '$shared/db' import { create_schema_hash, write_schema_hash } from './utils/schema' +import { devlog } from '$shared/utils' const schema_folder = path.resolve('databases', 'catdat', 'schema') @@ -13,7 +14,7 @@ setup() function setup() { console.info('\n--- Setup CatDat database ---') - const db = get_client() + const db = get_client({ readonly: false }) const files = fs .readdirSync(schema_folder, 'utf8') @@ -26,7 +27,7 @@ function setup() { for (const file of files) { const sql = fs.readFileSync(path.join(schema_folder, file), 'utf8') - console.info(`Apply: ${file}`) + devlog(`Apply: ${file}`) try { db.exec(sql) @@ -39,5 +40,5 @@ function setup() { const hash = create_schema_hash() write_schema_hash(hash) - console.info('Setup complete') + devlog('Setup complete') } diff --git a/databases/catdat/scripts/test.ts b/databases/catdat/scripts/test.ts index f34ee047d..1e501574d 100644 --- a/databases/catdat/scripts/test.ts +++ b/databases/catdat/scripts/test.ts @@ -12,14 +12,14 @@ import id_Set_expected from './expected-data/id_Set.json' import decided_categories from './expected-data/decided-categories.json' import decided_functors from './expected-data/decided-functors.json' import decided_morphisms from './expected-data/decided-morphisms.json' -import { capitalize } from './utils/helpers' -import { get_client } from './utils/db' -import { PLURALS, STRUCTURE_TYPES, type StructureType } from './config' +import { capitalize, devlog } from '$shared/utils' +import { get_client } from '$shared/db' +import { STRUCTURE_TYPES, type StructureType, PLURALS } from '$shared/config' import fs from 'node:fs' import path from 'node:path' -import { decode_property_ID } from '../../../src/lib/commons/property.utils' +import { decode_property_ID } from '$shared/property.utils' -const db = get_client() +const db = get_client({ readonly: true }) execute_tests() @@ -28,10 +28,12 @@ execute_tests() */ function execute_tests() { try { - console.info('--- Test link targets ---') + console.info('--- Database Tests ---') + + devlog('\n--- Test link targets ---') check_link_targets_exist() - console.info('\n--- Test categories ---') + devlog('\n--- Test categories ---') test_mutual_structure_duals('category') test_positivity('1', 'category') @@ -42,7 +44,7 @@ function execute_tests() { 'category' ) - console.info('\n--- Test functors ---') + devlog('\n--- Test functors ---') test_positivity('id_Set', 'functor') test_mutual_property_duals('functor') @@ -52,7 +54,7 @@ function execute_tests() { 'functor' ) - console.info('\n--- Test morphisms ---') + devlog('\n--- Test morphisms ---') test_positivity('id_X', 'morphism') test_decided_structures(decided_morphisms, 'morphism') @@ -91,7 +93,7 @@ function test_mutual_structure_duals(type: StructureType) { } } - console.info(`✅ ${capitalize(PLURALS[type])} are mutually dual`) + devlog(`✅ ${capitalize(PLURALS[type])} are mutually dual`) } /** @@ -113,11 +115,11 @@ function test_positivity(structure_id: string, type: StructureType) { ) } - console.info(`✅ The ${type} ${structure_id} has no unsatisfied properties`) + devlog(`✅ The ${type} ${structure_id} has no unsatisfied properties`) } /** - * Tests for all properties p,q of categories or functors that + * Tests for all properties p,q of structures that * if p is dual to q, then q is dual to p. */ function test_mutual_property_duals(type: StructureType) { @@ -141,11 +143,11 @@ function test_mutual_property_duals(type: StructureType) { } } - console.info(`✅ ${capitalize(type)} properties are mutually dual`) + devlog(`✅ ${capitalize(type)} properties are mutually dual`) } /** - * Tests that for a specified list of categories or functors all properties have + * Tests that for a specified list of structures all properties have * been decided. If this test fails, property assignments or implications are missing. */ function test_decided_structures(structure_ids: string[], type: StructureType) { @@ -168,12 +170,12 @@ function test_decided_structures(structure_ids: string[], type: StructureType) { ) } - console.info(`✅ All properties have been decided for ${structure_id}`) + devlog(`✅ All properties have been decided for ${structure_id}`) } } /** - * Tests if selected categories or functors behave as expected: + * Tests if selected structures behave as expected: * All of their properties in the database have to match those in the * respective JSON files in the subfolder "expected-data". * We exclude undecidable properties here. @@ -199,7 +201,7 @@ function test_properties_of_selected_structures( throw new Error(`❌ Incorrect property of ${structure_id}: ${property_id}`) } - console.info(`✅ Properties of ${structure_id} are correct`) + devlog(`✅ Properties of ${structure_id} are correct`) } } @@ -285,5 +287,5 @@ function check_link_targets_exist() { } } - console.info(`✅ Link targets exist`) + devlog(`✅ Link targets exist`) } diff --git a/databases/catdat/scripts/utils/db.ts b/databases/catdat/scripts/utils/db.ts deleted file mode 100644 index a2b477874..000000000 --- a/databases/catdat/scripts/utils/db.ts +++ /dev/null @@ -1,9 +0,0 @@ -import Database from 'better-sqlite3' -import path from 'node:path' - -export function get_client() { - const db_path = path.resolve('databases', 'catdat', 'catdat.db') - const db = new Database(db_path, { readonly: false }) - db.pragma('foreign_keys = ON') - return db -} diff --git a/databases/catdat/scripts/utils/helpers.ts b/databases/catdat/scripts/utils/helpers.ts deleted file mode 100644 index 7d66d9e28..000000000 --- a/databases/catdat/scripts/utils/helpers.ts +++ /dev/null @@ -1,27 +0,0 @@ -export function are_equal_sets(a: Set, b: Set) { - return a.size === b.size && [...a].every((el) => b.has(el)) -} - -export function is_subset(a: Set, b: Set, options?: { exception: T }) { - for (const x of a) { - if (x !== options?.exception && !b.has(x)) return false - } - return true -} - -export function capitalize(txt: string) { - return txt[0].toUpperCase() + txt.slice(1) -} - -export function parse_json_set(json: string): Set { - return new Set(JSON.parse(json)) -} - -export function parse_nested_json_set(json: string): Partial>> { - const obj = JSON.parse(json) - const result: Partial>> = {} - for (const key in obj) { - result[key] = parse_json_set(obj[key]) - } - return result -} diff --git a/databases/catdat/scripts/utils/implications.ts b/databases/catdat/scripts/utils/implications.ts index 5c02874ef..2d5f6a5a8 100644 --- a/databases/catdat/scripts/utils/implications.ts +++ b/databases/catdat/scripts/utils/implications.ts @@ -1,91 +1,7 @@ -import { type Database } from 'better-sqlite3' import type { PropertyMeta } from './properties' -import { StructureType } from '../config' -import { parse_nested_json_set, parse_json_set } from './helpers' -import { get_property_label } from '../../../../src/lib/commons/property.utils' - -export type NormalizedImplication = { - id: string - assumptions: Set - conclusion: string - mapped_assumptions?: Partial>> -} - -/** - * Implications have the form - * P_1 + ... + P_n ===> Q_1 + ... + Q_m - * or - * P_1 + ... + P_n <===> Q_1 + ... + Q_m. - * This function decomposes them into normalized implications, - * which have the form - * P_1 + ... + P_n ===> Q. - */ -export function get_normalized_implications( - db: Database, - type: StructureType -): NormalizedImplication[] { - const implications_db = db - .prepare< - [StructureType], - { - id: string - is_equivalence: 0 | 1 - assumptions: string - conclusions: string - mapped_assumptions: string - } - >( - `SELECT - id, - is_equivalence, - assumptions, - conclusions, - mapped_assumptions - FROM implications_view i - WHERE i.type = ?` - ) - .all(type) - - const implications: NormalizedImplication[] = [] - - for (const impl of implications_db) { - const assumptions = parse_json_set(impl.assumptions) - const conclusions = parse_json_set(impl.conclusions) - const mapped_assumptions = parse_nested_json_set(impl.mapped_assumptions) - - for (const conclusion of conclusions) { - const implication: NormalizedImplication = { - id: impl.id, - assumptions, - conclusion - } - - if (Object.keys(mapped_assumptions).length > 0) { - implication.mapped_assumptions = mapped_assumptions - } - - implications.push(implication) - } - - if (impl.is_equivalence) { - for (const assumption of assumptions) { - const implication: NormalizedImplication = { - id: impl.id, - assumptions: conclusions, - conclusion: assumption - } - - if (Object.keys(mapped_assumptions).length > 0) { - implication.mapped_assumptions = mapped_assumptions - } - - implications.push(implication) - } - } - } - - return implications -} +import { type StructureType } from '$shared/config' +import { get_property_label } from '$shared/property.utils' +import { type NormalizedImplication } from '$shared/implications' function get_assumption_string( implication: NormalizedImplication, diff --git a/databases/catdat/scripts/utils/properties.ts b/databases/catdat/scripts/utils/properties.ts index cdfcfa84c..df629bccb 100644 --- a/databases/catdat/scripts/utils/properties.ts +++ b/databases/catdat/scripts/utils/properties.ts @@ -1,5 +1,5 @@ import { type Database } from 'better-sqlite3' -import type { StructureType } from '../config' +import { type StructureType } from '$shared/config' /** * Property of a categorical structure @@ -38,8 +38,7 @@ export function get_properties_dict(db: Database, type: StructureType) { /** * Returns a dictionary with all assigned properties of a list of structures - * (categories or functors), grouped by id and - * value (satisfied / unsatisfied / undecidable). + * grouped by id and value (satisfied / unsatisfied / undecidable). */ export function get_property_assignments( db: Database, @@ -123,9 +122,7 @@ export function get_property_assignments_by_deduction( is_satisfied, is_deduced FROM property_assignments - WHERE - type = ? - AND is_satisfied IS NOT NULL` + WHERE type = ? AND is_satisfied IS NOT NULL` ) .all(type) diff --git a/databases/catdat/scripts/utils/seed.helpers.ts b/databases/catdat/scripts/utils/seed.helpers.ts index 1b04e7d2c..933675168 100644 --- a/databases/catdat/scripts/utils/seed.helpers.ts +++ b/databases/catdat/scripts/utils/seed.helpers.ts @@ -2,6 +2,7 @@ import type { Database } from 'better-sqlite3' import path from 'node:path' import fs from 'node:fs' import YAML from 'yaml' +import { devlog } from '$shared/utils' function read_yaml_file(...parts: string[]): T { const content = fs.readFileSync(path.join(...parts), 'utf8') @@ -21,7 +22,7 @@ export function seed_file( file: string, insert: (item: T) => void ) { - console.info(`\nSeed ${label} ...`) + devlog(`\nSeed ${label} ...`) const item = read_yaml_file(file) const tx = db.transaction(() => { @@ -43,7 +44,7 @@ export function seed_files( folder: string, insert: (item: T) => void ) { - console.info(`\nSeed ${label} ...`) + devlog(`\nSeed ${label} ...`) const files = get_yaml_files(folder) @@ -51,7 +52,7 @@ export function seed_files( db.pragma('defer_foreign_keys = ON') for (const file of files) { - console.info(`Seed: ${file}`) + devlog(`Seed: ${file}`) const item = read_yaml_file(folder, file) insert(item) diff --git a/databases/catdat/scripts/utils/structures.ts b/databases/catdat/scripts/utils/structures.ts index 565f848e9..17c6848e6 100644 --- a/databases/catdat/scripts/utils/structures.ts +++ b/databases/catdat/scripts/utils/structures.ts @@ -1,5 +1,5 @@ import { type Database } from 'better-sqlite3' -import { StructureType, TABLES } from '../config' +import { type StructureType } from '$shared/config' /** * Type for various types of categorical structures (category, functor, ...) @@ -8,7 +8,17 @@ export type StructureMeta = { id: string name: string dual?: string | null - associated_satisfied_properties?: Record> + associated_satisfied_properties?: Partial>> +} + +/** + * Dictionary associating to every structure type the name of the table. + * Currently, this is the same as the plural. + */ +const TABLES: Record = { + category: 'categories', + functor: 'functors', + morphism: 'morphisms' } /** @@ -30,7 +40,11 @@ export function get_structures(db: Database, type: StructureType): StructureMeta .all(type) const structure_maps = db - .prepare<[StructureType], string>(`SELECT map FROM structure_maps WHERE type = ?`) + .prepare<[StructureType], string>( + `SELECT map + FROM structure_maps + WHERE type = ?` + ) .pluck() .all(type) diff --git a/databases/tsconfig.json b/databases/tsconfig.json new file mode 100644 index 000000000..0bee711b6 --- /dev/null +++ b/databases/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleResolution": "bundler", + "paths": { + "$shared/*": ["../shared/*"] + }, + "strict": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "noEmit": true + }, + "include": ["**/*.ts"] +} diff --git a/package.json b/package.json index aac44ed52..6e756261c 100644 --- a/package.json +++ b/package.json @@ -17,16 +17,16 @@ "env:check": "tsx scripts/env.check", "db:shell": "sqlite3 databases/catdat/catdat.db", "db:kill": "rm -rf databases/catdat/catdat.db", - "db:setup:catdat": "pnpm db:kill && tsx databases/catdat/scripts/setup.ts", - "db:setup": "pnpm db:kill && tsx databases/catdat/scripts/setup.ts && tsx databases/app/scripts/setup.ts", - "db:seed": "tsx databases/catdat/scripts/seed.ts", - "db:deduce": "tsx databases/catdat/scripts/deduce.ts", - "db:test": "tsx databases/catdat/scripts/test.ts", + "db:setup:catdat": "pnpm db:kill && tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/setup.ts", + "db:setup": "pnpm db:kill && tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/setup.ts && tsx --tsconfig databases/tsconfig.json databases/app/scripts/setup.ts", + "db:seed": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/seed.ts", + "db:deduce": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/deduce.ts", + "db:test": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/test.ts", "db:snapshot": "cp databases/catdat/catdat.db static/databases/catdat-snapshot.db", "db:update": "pnpm db:seed && pnpm db:deduce && pnpm db:test && pnpm db:snapshot", - "db:watch": "tsx databases/catdat/scripts/watch.ts", - "db:redundancies": "tsx databases/catdat/scripts/redundancies.ts", - "db:proof-length": "tsx databases/catdat/scripts/proof-length.ts", + "db:watch": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/watch.ts", + "db:redundancies": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/redundancies.ts", + "db:proof-length": "tsx --tsconfig databases/tsconfig.json databases/catdat/scripts/proof-length.ts", "e2e": "PUBLIC_PLAYWRIGHT=true pnpm exec playwright test", "e2e:debug": "PUBLIC_PLAYWRIGHT=true pnpm exec playwright test --debug", "e2e:ui": "PUBLIC_PLAYWRIGHT=true pnpm exec playwright test --ui" diff --git a/src/lib/commons/structures.ts b/shared/config.ts similarity index 65% rename from src/lib/commons/structures.ts rename to shared/config.ts index a857ee361..9e4a2311f 100644 --- a/src/lib/commons/structures.ts +++ b/shared/config.ts @@ -6,16 +6,10 @@ export function is_structure_type(txt: string): txt is StructureType { return (STRUCTURE_TYPES as readonly string[]).includes(txt) } +export const STRUCTURE_TYPES_WITH_DUALS: StructureType[] = ['category'] + export const PLURALS: Record = { category: 'categories', functor: 'functors', morphism: 'morphisms' } - -export function get_selected_type(pathname: string): StructureType { - for (const type of STRUCTURE_TYPES) { - const matches = pathname.startsWith(`/${type}`) - if (matches) return type - } - return 'category' -} diff --git a/shared/db.ts b/shared/db.ts new file mode 100644 index 000000000..19cf92365 --- /dev/null +++ b/shared/db.ts @@ -0,0 +1,10 @@ +import Database from 'better-sqlite3' +import path from 'node:path' + +const db_path = path.resolve('databases', 'catdat', 'catdat.db') + +export function get_client(options: { readonly: boolean }) { + const db = new Database(db_path, options) + db.pragma('foreign_keys = ON') + return db +} diff --git a/shared/deduction.utils.ts b/shared/deduction.utils.ts new file mode 100644 index 000000000..4fe21763f --- /dev/null +++ b/shared/deduction.utils.ts @@ -0,0 +1,134 @@ +import { type NormalizedImplication } from './implications' +import { is_subset } from './utils' + +export function deduce_properties( + satisfied_properties: Set, + implications: NormalizedImplication[], + on_discover: (implication: NormalizedImplication) => { + proof: ProofType + stop: boolean + }, + associated_satisfied_properties?: Partial>> +) { + const found = new Set() + const proofs: Partial> = {} + const deduced_satisfied_properties = new Set(satisfied_properties) + + while (true) { + const newly_found = new Set() + + for (const implication of implications) { + const is_valid = + is_subset(implication.assumptions, deduced_satisfied_properties) && + !deduced_satisfied_properties.has(implication.conclusion) && + !newly_found.has(implication.conclusion) + + if (!is_valid) continue + + if (!is_applicable(implication, associated_satisfied_properties)) continue + + newly_found.add(implication.conclusion) + found.add(implication.conclusion) + + const { proof, stop } = on_discover(implication) + + proofs[implication.conclusion] = proof + + if (stop) { + for (const p of newly_found) deduced_satisfied_properties.add(p) + return { + deduced_satisfied_properties, + found, + proofs, + stop_property: implication.conclusion + } + } + } + + for (const p of newly_found) deduced_satisfied_properties.add(p) + + if (!newly_found.size) break + } + + return { deduced_satisfied_properties, found, proofs } +} + +export function refute_properties( + satisfied_properties: Set, + unsatisfied_properties: Set, + implications: NormalizedImplication[], + on_discover: ( + implication: NormalizedImplication, + property: string + ) => { proof: ProofType; stop: boolean }, + associated_satisfied_properties?: Partial>> +) { + const found = new Set() + const proofs: Partial> = {} + const deduced_unsatisfied_properties = new Set(unsatisfied_properties) + + while (true) { + const newly_found = new Set() + + for (const implication of implications) { + if (!deduced_unsatisfied_properties.has(implication.conclusion)) continue + + for (const property of implication.assumptions) { + const is_valid = + !deduced_unsatisfied_properties.has(property) && + !newly_found.has(property) && + is_subset(implication.assumptions, satisfied_properties, { + exception: property + }) + + if (!is_valid) continue + + if (!is_applicable(implication, associated_satisfied_properties)) continue + + if (satisfied_properties.has(property)) { + throw new Error(`Contradiction has been found for: ${property}`) + } + + newly_found.add(property) + found.add(property) + + const discovery = on_discover(implication, property) + + if (discovery.proof !== undefined) { + proofs[property] = discovery.proof + } + + if (discovery.stop) { + for (const p of newly_found) deduced_unsatisfied_properties.add(p) + return { + deduced_unsatisfied_properties, + found, + proofs, + stop_property: property + } + } + } + } + + for (const property of newly_found) deduced_unsatisfied_properties.add(property) + + if (!newly_found.size) break + } + + return { deduced_unsatisfied_properties, found, proofs } +} + +function is_applicable( + implication: NormalizedImplication, + associated_satisfied_properties?: Partial>> +) { + return ( + !implication.mapped_assumptions || + Object.keys(implication.mapped_assumptions).every((key) => { + return is_subset( + implication.mapped_assumptions?.[key] ?? new Set(), + associated_satisfied_properties?.[key] ?? new Set() + ) + }) + ) +} diff --git a/shared/implications.ts b/shared/implications.ts new file mode 100644 index 000000000..9fbfb749f --- /dev/null +++ b/shared/implications.ts @@ -0,0 +1,92 @@ +import { type Database } from 'better-sqlite3' +import { type StructureType } from './config' +import { parse_json_set, parse_nested_json_set } from './utils' + +export type NormalizedImplication = { + id: string + assumptions: Set + conclusion: string + mapped_assumptions?: Partial>> +} + +/** + * Implications have the form + * P_1 + ... + P_n ===> Q_1 + ... + Q_m + * or + * P_1 + ... + P_n <===> Q_1 + ... + Q_m. + * This function decomposes them into normalized implications, + * which have the form + * P_1 + ... + P_n ===> Q. + */ +export function get_normalized_implications( + db: Database, + type: StructureType +): NormalizedImplication[] { + const implications_db = db + .prepare< + [StructureType], + { + id: string + is_equivalence: 0 | 1 + assumptions: string + conclusions: string + mapped_assumptions: string + } + >( + `SELECT + id, + is_equivalence, + assumptions, + conclusions, + mapped_assumptions + FROM implications_view + WHERE type = ?` + ) + .all(type) + + const implications: NormalizedImplication[] = [] + + for (const impl of implications_db) { + const assumptions = parse_json_set(impl.assumptions) + const conclusions = parse_json_set(impl.conclusions) + const mapped_assumptions = parse_nested_json_set(impl.mapped_assumptions) + + const has_mapped_assumptions = Object.keys(mapped_assumptions).length > 0 + + for (const conclusion of conclusions) { + const implication: NormalizedImplication = { + id: impl.id, + assumptions, + conclusion + } + + if (has_mapped_assumptions) { + implication.mapped_assumptions = mapped_assumptions + } + + implications.push(implication) + } + + if (impl.is_equivalence) { + for (const assumption of assumptions) { + const implication: NormalizedImplication = { + id: impl.id, + assumptions: conclusions, + conclusion: assumption + } + + if (has_mapped_assumptions) { + implication.mapped_assumptions = mapped_assumptions + } + + implications.push(implication) + } + } + } + + return implications +} + +export function stringify_implication(implication: NormalizedImplication) { + return `${[...implication.assumptions].join(' ∧ ')} ⟹ ${implication.conclusion}` +} diff --git a/src/lib/commons/property.utils.ts b/shared/property.utils.ts similarity index 94% rename from src/lib/commons/property.utils.ts rename to shared/property.utils.ts index 305e35989..88eef66f0 100644 --- a/src/lib/commons/property.utils.ts +++ b/shared/property.utils.ts @@ -1,4 +1,4 @@ -import type { StructureType } from './types' +import type { StructureType } from './config' const ENCODE_MAP: Record = { ' ': '_', diff --git a/shared/utils.ts b/shared/utils.ts new file mode 100644 index 000000000..0cb45d731 --- /dev/null +++ b/shared/utils.ts @@ -0,0 +1,79 @@ +export function is_string_array(arr: unknown): arr is string[] { + return Array.isArray(arr) && arr.every((el) => typeof el === 'string') +} + +export function is_object(obj: unknown): obj is Record { + return obj != null && obj.constructor.name === 'Object' +} + +export function are_equal_sets(a: Set, b: Set) { + return a.size === b.size && [...a].every((el) => b.has(el)) +} + +export function is_subset(a: Set, b: Set, options?: { exception: T }) { + for (const x of a) { + if (x !== options?.exception && !b.has(x)) return false + } + return true +} + +export function are_disjoint(lists: T[][], transform: (x: T) => S) { + for (let i = 0; i < lists.length; i++) { + const a = lists[i].map(transform) + for (let j = i + 1; j < lists.length; j++) { + const b = lists[j].map(transform) + if (a.some((x) => b.includes(x))) return false + } + } + return true +} + +export const sleep = (delay: number) => new Promise((res) => setTimeout(res, delay)) + +export function capitalize(txt: string) { + return txt[0].toUpperCase() + txt.slice(1) +} + +export function pluralize(count: number, forms: { one: string; other: string }) { + const word = count === 1 ? forms.one : forms.other + return word.replace('{count}', String(count)) +} + +/** + * Removes accents from letters and transforms to lowercase + */ +export function normalize_text(txt: string) { + return txt + .toLowerCase() + .normalize('NFD') + .replace(/\p{Diacritic}/gu, '') +} + +export function parse_json_set(json: string): Set { + return new Set(JSON.parse(json)) +} + +export function parse_nested_json_set(json: string): Partial>> { + const obj = JSON.parse(json) + const result: Partial>> = {} + for (const key in obj) { + result[key] = parse_json_set(obj[key]) + } + return result +} + +export function to_placeholders(arr: string[]): string { + return `(${arr.map(() => '?').join(', ')})` +} + +export function strip_math(s: string) { + return s.replaceAll('$', '') +} + +export function add_math(s: string) { + return `$${s}$` +} + +export function devlog(msg: string) { + if (!process.env.CI) console.info(msg) +} diff --git a/src/components/ImplicationItem.svelte b/src/components/ImplicationItem.svelte index 2404f7866..4e4932536 100644 --- a/src/components/ImplicationItem.svelte +++ b/src/components/ImplicationItem.svelte @@ -6,7 +6,7 @@ faPlus } from '@fortawesome/free-solid-svg-icons' import Fa from 'svelte-fa' - import { get_property_url } from '$lib/commons/property.utils' + import { get_property_url } from '$shared/property.utils' import type { ImplicationDisplay, StructureType } from '$lib/commons/types' type Props = { @@ -18,7 +18,7 @@ let { type, implication, highlighted_property }: Props = $props() let has_additional_assumptions = $derived( - Object.values(implication.mapped_assumptions).some((list) => list?.length) + Object.values(implication.mapped_assumptions).some((list) => list?.size) ) diff --git a/src/components/PropertyAssignmentList.svelte b/src/components/PropertyAssignmentList.svelte index 90f5c7265..1e204186c 100644 --- a/src/components/PropertyAssignmentList.svelte +++ b/src/components/PropertyAssignmentList.svelte @@ -9,7 +9,7 @@ StructureType } from '$lib/commons/types' import { assignment_level } from '$lib/states/assignment_level.svelte' - import { pluralize } from '$lib/client/utils' + import { pluralize } from '$shared/utils' type Props = { type: StructureType diff --git a/src/components/PropertyList.svelte b/src/components/PropertyList.svelte index 5ef66a527..1c49878a1 100644 --- a/src/components/PropertyList.svelte +++ b/src/components/PropertyList.svelte @@ -1,5 +1,5 @@ diff --git a/src/routes/[type]-comparison/+page.server.ts b/src/routes/[type]-comparison/+page.server.ts index 6164bf3ce..421c78d3d 100644 --- a/src/routes/[type]-comparison/+page.server.ts +++ b/src/routes/[type]-comparison/+page.server.ts @@ -1,4 +1,4 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_structures } from '$lib/server/fetchers/structures' import { error } from '@sveltejs/kit' diff --git a/src/routes/[type]-comparison/[...ids]/+page.server.ts b/src/routes/[type]-comparison/[...ids]/+page.server.ts index 326717db7..f39bd4f4f 100644 --- a/src/routes/[type]-comparison/[...ids]/+page.server.ts +++ b/src/routes/[type]-comparison/[...ids]/+page.server.ts @@ -1,6 +1,6 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_comparison_result } from '$lib/server/fetchers/comparison' -import { cache_page } from '$lib/server/utils' +import { cache_page } from '$lib/server/cache' import { error } from '@sveltejs/kit' export const prerender = false diff --git a/src/routes/[type]-implication/[id]/+page.server.ts b/src/routes/[type]-implication/[id]/+page.server.ts index 5dc67fa23..acb0de2ad 100644 --- a/src/routes/[type]-implication/[id]/+page.server.ts +++ b/src/routes/[type]-implication/[id]/+page.server.ts @@ -1,4 +1,4 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_implication } from '$lib/server/fetchers/implication' import { render_nested_formulas } from '$lib/server/formulas' import { error } from '@sveltejs/kit' diff --git a/src/routes/[type]-implications/+page.server.ts b/src/routes/[type]-implications/+page.server.ts index d8e406583..ccfb65564 100644 --- a/src/routes/[type]-implications/+page.server.ts +++ b/src/routes/[type]-implications/+page.server.ts @@ -1,6 +1,6 @@ import { render_nested_formulas } from '$lib/server/formulas' import { fetch_implications } from '$lib/server/fetchers/implications' -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { error } from '@sveltejs/kit' export const load = (event) => { diff --git a/src/routes/[type]-list/+page.server.ts b/src/routes/[type]-list/+page.server.ts index a8ad4c0b6..ebc7eb0ac 100644 --- a/src/routes/[type]-list/+page.server.ts +++ b/src/routes/[type]-list/+page.server.ts @@ -1,7 +1,8 @@ -import { is_structure_type, STRUCTURE_TYPES } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_structures_and_tags } from '$lib/server/fetchers/structures' import { error } from '@sveltejs/kit' import type { EntryGenerator } from './$types' +import { STRUCTURE_TYPES } from '$shared/config' export const entries: EntryGenerator = () => { return STRUCTURE_TYPES.map((type) => ({ type })) diff --git a/src/routes/[type]-list/[tag]/+page.server.ts b/src/routes/[type]-list/[tag]/+page.server.ts index b167a39c3..3050f2c03 100644 --- a/src/routes/[type]-list/[tag]/+page.server.ts +++ b/src/routes/[type]-list/[tag]/+page.server.ts @@ -1,5 +1,5 @@ import { fetch_tagged_structures } from '$lib/server/fetchers/structures' -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { error } from '@sveltejs/kit' import type { EntryGenerator } from './$types' import { fetch_structure_tags } from '$lib/server/fetchers/tags' diff --git a/src/routes/[type]-properties/+page.server.ts b/src/routes/[type]-properties/+page.server.ts index c9b3687fe..07950b57d 100644 --- a/src/routes/[type]-properties/+page.server.ts +++ b/src/routes/[type]-properties/+page.server.ts @@ -1,4 +1,4 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_grouped_properties_and_tags } from '$lib/server/fetchers/properties' import { error } from '@sveltejs/kit' diff --git a/src/routes/[type]-properties/[tag]/+page.server.ts b/src/routes/[type]-properties/[tag]/+page.server.ts index 28049e913..d5508d79f 100644 --- a/src/routes/[type]-properties/[tag]/+page.server.ts +++ b/src/routes/[type]-properties/[tag]/+page.server.ts @@ -1,4 +1,4 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { fetch_tagged_properties } from '$lib/server/fetchers/properties' import { error } from '@sveltejs/kit' import type { EntryGenerator } from './$types' diff --git a/src/routes/[type]-property/[id]/+page.server.ts b/src/routes/[type]-property/[id]/+page.server.ts index 51bb06fe2..c78782d44 100644 --- a/src/routes/[type]-property/[id]/+page.server.ts +++ b/src/routes/[type]-property/[id]/+page.server.ts @@ -1,7 +1,7 @@ import { render_nested_formulas } from '$lib/server/formulas' -import { decode_property_ID } from '$lib/commons/property.utils' +import { decode_property_ID } from '$shared/property.utils' import { fetch_property } from '$lib/server/fetchers/property' -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { error } from '@sveltejs/kit' export const load = (event) => { diff --git a/src/routes/[type]-search/+page.server.ts b/src/routes/[type]-search/+page.server.ts index 3bdfc3016..963f48f24 100644 --- a/src/routes/[type]-search/+page.server.ts +++ b/src/routes/[type]-search/+page.server.ts @@ -1,4 +1,4 @@ -import { is_structure_type } from '$lib/commons/structures' +import { is_structure_type } from '$shared/config' import { get_property_ids } from '$lib/server/fetchers/properties' import { error } from '@sveltejs/kit' diff --git a/src/routes/[type]-search/+page.svelte b/src/routes/[type]-search/+page.svelte index de71fe568..e19c21d44 100644 --- a/src/routes/[type]-search/+page.svelte +++ b/src/routes/[type]-search/+page.svelte @@ -1,5 +1,5 @@ diff --git a/svelte.config.js b/svelte.config.js index 721beb7a1..2e3148e60 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -7,7 +7,8 @@ const config = { kit: { alias: { $components: './src/components', - $pages: './src/pages' + $pages: './src/pages', + $shared: './shared' }, adapter: adapter() }