diff --git a/.vscode/settings.json b/.vscode/settings.json
index d4dc5d2c..e0ea4711 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -19,6 +19,8 @@
],
"cSpell.words": [
"abelian",
+ "abelianization",
+ "abelianize",
"Adamek",
"algébriques",
"anneaux",
@@ -43,11 +45,14 @@
"coequalizer",
"coequalizers",
"cofiltered",
+ "cofinitary",
"cogenerates",
"cogenerator",
"cokernel",
"colimit",
"colimits",
+ "comonad",
+ "comonadic",
"conormal",
"coprime",
"coproduct",
@@ -121,6 +126,7 @@
"rng",
"rngs",
"Rosicky",
+ "saft",
"Schapira",
"semisimple",
"setoid",
diff --git a/database/data/000_clear.sql b/database/data/000_clear.sql
index b4ca5475..bb8c4fdf 100644
--- a/database/data/000_clear.sql
+++ b/database/data/000_clear.sql
@@ -21,6 +21,17 @@ DELETE FROM tags;
DELETE FROM related_properties;
DELETE FROM properties;
+
+DELETE FROM functor_property_assignments;
+DELETE FROM functor_properties;
+DELETE FROM functors;
+
DELETE FROM prefixes;
+DELETE FROM functor_implication_assumptions;
+DELETE FROM functor_implication_conclusions;
+DELETE FROM functor_implication_source_assumptions;
+DELETE FROM functor_implication_target_assumptions;
+DELETE FROM functor_implications;
+
PRAGMA foreign_keys = ON;
\ No newline at end of file
diff --git a/database/data/004_prefixes.sql b/database/data/004_prefixes.sql
index 26e088f2..93aff501 100644
--- a/database/data/004_prefixes.sql
+++ b/database/data/004_prefixes.sql
@@ -4,4 +4,5 @@ INSERT INTO prefixes (prefix, negation) VALUES
('is an', 'is not an'),
('has', 'does not have'),
('has a', 'does not have a'),
- ('has an', 'does not have an');
\ No newline at end of file
+ ('has an', 'does not have an'),
+ ('', 'does not');
\ No newline at end of file
diff --git a/database/data/020_functor-properties.sql b/database/data/020_functor-properties.sql
new file mode 100644
index 00000000..86e8dc39
--- /dev/null
+++ b/database/data/020_functor-properties.sql
@@ -0,0 +1,201 @@
+INSERT INTO functor_properties (
+ id,
+ prefix,
+ description,
+ nlab_link,
+ invariant_under_equivalences,
+ dual_property_id
+)
+VALUES
+ (
+ 'faithful',
+ 'is',
+ 'A functor is faithful when it is injective on Hom-sets: If $F(f)=F(g)$, then $f=g$.',
+ 'https://ncatlab.org/nlab/show/faithful+functor',
+ TRUE,
+ 'faithful'
+ ),
+ (
+ 'full',
+ 'is',
+ 'A functor is full when it is surjective on Hom-sets: Every morphism $F(A) \to F(B)$ is induced by a morphism $A \to B$.',
+ 'https://ncatlab.org/nlab/show/full+functor',
+ TRUE,
+ 'full'
+ ),
+ (
+ 'essentially surjective',
+ 'is',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is essentially surjective when every object $Y \in \mathcal{D}$ is isomorphic to $F(X)$ for some $X \in \mathcal{C}$.',
+ 'https://ncatlab.org/nlab/show/essentially+surjective+functor',
+ TRUE,
+ 'essentially surjective'
+ ),
+ (
+ 'equivalence',
+ 'is an',
+ 'A functor is an equivalence if it has a pseudo-inverse functor.',
+ 'https://ncatlab.org/nlab/show/equivalence+of+categories',
+ TRUE,
+ 'equivalence'
+ ),
+ (
+ 'continuous',
+ 'is',
+ 'A functor is continuous when it preserves all small limits.',
+ 'https://ncatlab.org/nlab/show/continuous+functor',
+ TRUE,
+ 'cocontinuous'
+ ),
+ (
+ 'cocontinuous',
+ 'is',
+ 'A functor is cocontinuous when it preserves all small colimits.',
+ 'https://ncatlab.org/nlab/show/cocontinuous+functor',
+ TRUE,
+ 'continuous'
+ ),
+ (
+ 'preserves products',
+ '',
+ 'A functor $F$ preserves products when for every family of objects $(A_i)$ in the source whose product $\prod_i A_i$ exists, also the product $\prod_i F(A_i)$ exists in the target and such that the canonical morphism $F(\prod_i A_i) \to \prod_i F(A_i)$ is an isomorphism.',
+ NULL,
+ TRUE,
+ 'preserves coproducts'
+ ),
+ (
+ 'preserves coproducts',
+ '',
+ 'A functor $F$ preserves coproducts when for every family of objects $(A_i)$ in the source whose coproduct $\prod_i A_i$ exists, also the coproduct $\coprod_i F(A_i)$ exists in the target and such that the canonical morphism $\coprod_i F(A_i) \to F(\coprod_i A_i)$ is an isomorphism.',
+ NULL,
+ TRUE,
+ 'preserves products'
+ ),
+ (
+ 'preserves finite products',
+ '',
+ 'A functor $F$ preserves finite products when for every finite family of objects $(A_i)$ in the source whose product $\prod_i A_i$ exists, also the product $\prod_i F(A_i)$ exists in the target and such that the canonical morphism $F(\prod_i A_i) \to \prod_i F(A_i)$ is an isomorphism.',
+ NULL,
+ TRUE,
+ 'preserves finite coproducts'
+ ),
+ (
+ 'preserves finite coproducts',
+ '',
+ 'A functor $F$ preserves finite coproducts when for every family of objects $(A_i)$ in the source whose coproduct $\prod_i A_i$ exists, also the coproduct $\coprod_i F(A_i)$ exists in the target and such that the canonical morphism $\coprod_i F(A_i) \to F(\coprod_i A_i)$ is an isomorphism.',
+ NULL,
+ TRUE,
+ 'preserves finite products'
+ ),
+ (
+ 'preserves terminal objects',
+ '',
+ 'A functor $F$ preserves terminal objects when it maps every terminal object to a terminal object. It is not assumed that the source category has a terminal object.',
+ NULL,
+ TRUE,
+ 'preserves initial objects'
+ ),
+ (
+ 'preserves initial objects',
+ '',
+ 'A functor $F$ preserves initial objects when it maps every initial object to an initial object. It is not assumed that the source category has a initial object.',
+ NULL,
+ TRUE,
+ 'preserves terminal objects'
+ ),
+ (
+ 'preserves equalizers',
+ '',
+ 'A functor $F$ preserves equalizers when for every parallel pair of morphisms $f,g : A \to B$ whose equalizer $i : E \to A$ exists, also $F(i) : F(E) \to F(A)$ is an equalizer of $F(f),F(g) : F(A) \to F(B)$.',
+ NULL,
+ TRUE,
+ 'preserves coequalizers'
+ ),
+ (
+ 'preserves coequalizers',
+ '',
+ 'A functor $F$ preserves coequalizers when for every parallel pair of morphisms $f,g : A \to B$ whose coequalizer $p : B \to Q$ exists, also $F(p) : F(B) \to F(Q)$ is an coequalizer of $F(f),F(g) : F(A) \to F(B)$.',
+ NULL,
+ TRUE,
+ 'preserves equalizers'
+ ),
+ (
+ 'left adjoint',
+ 'is a',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is a left adjoint when there is a functor $G : \mathcal{D} \to \mathcal{C}$ such that there are natural bijections $\hom(F(A),B) \cong \hom(A,G(B))$.',
+ 'https://ncatlab.org/nlab/show/left+adjoint',
+ TRUE,
+ 'right adjoint'
+ ),
+ (
+ 'right adjoint',
+ 'is a',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is a right adjoint when there is a functor $G : \mathcal{D} \to \mathcal{C}$ such that there are natural bijections $\hom(G(A),B) \cong \hom(A,F(B))$.',
+ 'https://ncatlab.org/nlab/show/right+adjoint',
+ TRUE,
+ 'left adjoint'
+ ),
+ (
+ 'monadic',
+ 'is',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is monadic when there is a monad $T$ on $\mathcal{D}$ such that $F$ is equivalent to the forgetful functor $U^T : \mathbf{Alg}(T) \to \mathcal{D}$.',
+ 'https://ncatlab.org/nlab/show/monadic+functor',
+ TRUE,
+ 'comonadic'
+ ),
+ (
+ 'comonadic',
+ 'is',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is comonadic when there is a comonad $T$ on $\mathcal{D}$ such that $F$ is equivalent to the forgetful functor $U^T : \mathbf{CoAlg}(T) \to \mathcal{D}$.',
+ 'https://ncatlab.org/nlab/show/comonadic+functor',
+ TRUE,
+ 'monadic'
+ ),
+ (
+ 'conservative',
+ 'is',
+ 'A functor $F : \mathcal{C} \to \mathcal{D}$ is conservative when it is isomorphic-reflecting: If $f$ is a morphism in $\mathcal{C}$ such that $F(f)$ is an isomorphism, then $f$ is an isomorphism.',
+ 'https://ncatlab.org/nlab/show/conservative+functor',
+ TRUE,
+ 'conservative'
+ ),
+ (
+ 'finitary',
+ 'is',
+ 'A functor is finitary when it preserves filtered colimits.',
+ 'https://ncatlab.org/nlab/show/finitary+functor',
+ TRUE,
+ 'cofinitary'
+ ),
+ (
+ 'cofinitary',
+ 'is',
+ 'A functor is cofinitary when it preserves filtered limits.',
+ NULL,
+ TRUE,
+ 'finitary'
+ ),
+ (
+ 'left exact',
+ 'is',
+ 'A functor is left exact when it preserves finite limits.',
+ 'https://ncatlab.org/nlab/show/exact+functor',
+ TRUE,
+ 'right exact'
+ ),
+ (
+ 'right exact',
+ 'is',
+ 'A functor is right exact when it preserves finite colimits.',
+ 'https://ncatlab.org/nlab/show/exact+functor',
+ TRUE,
+ 'left exact'
+ ),
+ (
+ 'exact',
+ 'is',
+ 'A functor is exact when it is left exact and right exact.',
+ 'https://ncatlab.org/nlab/show/exact+functor',
+ TRUE,
+ 'exact'
+ );
diff --git a/database/data/021_functor-implications.sql b/database/data/021_functor-implications.sql
new file mode 100644
index 00000000..cf9c32a9
--- /dev/null
+++ b/database/data/021_functor-implications.sql
@@ -0,0 +1,142 @@
+INSERT INTO functor_implication_input (
+ id,
+ assumptions,
+ source_assumptions,
+ target_assumptions,
+ conclusions,
+ reason,
+ is_equivalence
+)
+VALUES
+ (
+ 'equivalence_criterion',
+ '["full", "faithful", "essentially surjective"]',
+ '[]',
+ '[]',
+ '["equivalence"]',
+ 'TBA.', -- FIXME
+ TRUE
+ ),
+ (
+ 'continuous_consequences',
+ '["continuous"]',
+ '[]',
+ '[]',
+ '["preserves products", "preserves equalizers", "cofinitary"]',
+ 'This is trivial.',
+ FALSE
+ ),
+ (
+ 'products_consequences',
+ '["preserves products"]',
+ '[]',
+ '[]',
+ '["preserves finite products"]',
+ 'This is trivial.',
+ FALSE
+ ),
+ (
+ 'finite_products_consequences',
+ '["preserves finite products"]',
+ '[]',
+ '[]',
+ '["preserves terminal objects"]',
+ 'This is trivial.',
+ FALSE
+ ),
+ (
+ -- TODO: remove this later after automatic dualization
+ 'dual_continuous_consequences',
+ '["cocontinuous"]',
+ '[]',
+ '[]',
+ '["preserves coproducts", "preserves coequalizers", "finitary"]',
+ 'This is trivial.',
+ FALSE
+ ),
+ (
+ 'continuous_criterion',
+ '["preserves products", "preserves equalizers"]',
+ '["products"]',
+ '[]',
+ '["continuous"]',
+ 'TBA.', -- FIXME
+ FALSE
+ ),
+ (
+ 'continuous_criterion_filtered',
+ '["left exact", "cofinitary"]',
+ '["finitely complete"]',
+ '[]',
+ '["continuous"]',
+ 'This is because every limit can be written as a filtered limit of finite limits.',
+ FALSE
+ ),
+ (
+ 'exact_definition',
+ '["exact"]',
+ '[]',
+ '[]',
+ '["left exact", "right exact"]',
+ 'This holds by definition.',
+ TRUE
+ ),
+ (
+ 'left_exact_consequences',
+ '["left exact"]',
+ '[]',
+ '[]',
+ '["preserves finite products", "preserves terminal objects"]',
+ 'Both finite products and terminal objects are special cases of finite limits.',
+ FALSE
+ ),
+ (
+ 'left_exact_criterion',
+ '["preserves finite products", "preserves equalizers"]',
+ '["finite products"]',
+ '[]',
+ '["left exact"]',
+ 'TBA.', -- FIXME
+ FALSE
+ ),
+ (
+ 'equivalence_consequences',
+ '["equivalence"]',
+ '[]',
+ '[]',
+ '["continuous", "right adjoint"]',
+ 'Easy.',
+ FALSE
+ ),
+ (
+ 'right_adjoint_consequences',
+ '["right adjoint"]',
+ '[]',
+ '[]',
+ '["continuous"]',
+ 'TBA.', -- FIXME
+ FALSE
+ ),
+ (
+ 'saft',
+ '["continuous"]',
+ '["complete", "locally small", "well-powered", "cogenerator"]',
+ '["locally small"]',
+ '["right adjoint"]',
+ 'This is the Special Adjoint Functor Theorem. The proof can be found, for example, at the nLab.',
+ FALSE
+ ),
+ (
+ 'monadic_consequences',
+ '["monadic"]',
+ '[]',
+ '[]',
+ '["faithful", "right adjoint", "conservative"]',
+ 'This is clear since the forgetful functor from the category of $T$-algebras has these properties, where $T$ is any monad.',
+ FALSE
+ );
+
+ -- TODO: remove this later:
+ UPDATE functor_implications
+ SET dualized_from = 'continuous_consequences'
+ WHERE id = 'dual_continuous_consequences';
\ No newline at end of file
diff --git a/database/data/022_functor-examples.sql b/database/data/022_functor-examples.sql
new file mode 100644
index 00000000..1e914727
--- /dev/null
+++ b/database/data/022_functor-examples.sql
@@ -0,0 +1,49 @@
+INSERT INTO functors (
+ id,
+ name,
+ source,
+ target,
+ description,
+ url
+)
+VALUES
+ (
+ 'id_Set',
+ 'identity functor on the category of sets',
+ 'Set',
+ 'Set',
+ 'Every category has an identity functor. Here, we specify that it is for the category of sets.',
+ 'https://ncatlab.org/nlab/show/identity+functor'
+ ),
+ (
+ 'abelianize',
+ 'abelianization functor for groups',
+ 'Grp',
+ 'Ab',
+ 'This functor maps a group $G$ to its abelianization $G/[G,G]$.',
+ 'https://ncatlab.org/nlab/show/abelianization'
+ ),
+ (
+ 'forget_vector',
+ 'forgetful functor for vector spaces',
+ 'Vect',
+ 'Set',
+ 'This functor maps a vector space to its underlying set.',
+ 'https://ncatlab.org/nlab/show/forgetful+functor'
+ ),
+ (
+ 'free_group',
+ 'free group functor',
+ 'Set',
+ 'Grp',
+ 'This functor maps a set to the free group on that set.',
+ 'https://ncatlab.org/nlab/show/free+functor'
+ ),
+ (
+ 'power_set_covariant',
+ 'covariant power set functor',
+ 'Set',
+ 'Set',
+ 'This functor maps a set $X$ to its power set $P(X)$ and a map of sets $f : X \to Y$ to the induced map $f_* : P(X) \to P(Y)$.',
+ 'https://ncatlab.org/nlab/show/power+set'
+ );
\ No newline at end of file
diff --git a/database/data/023_functor-satisfied-properties.sql b/database/data/023_functor-satisfied-properties.sql
new file mode 100644
index 00000000..691e2249
--- /dev/null
+++ b/database/data/023_functor-satisfied-properties.sql
@@ -0,0 +1,46 @@
+CREATE TEMP TABLE functor_properties (
+ functor_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ reason TEXT NOT NULL
+);
+
+INSERT INTO functor_properties (
+ functor_id,
+ property_id,
+ reason
+)
+VALUES
+ (
+ 'id_Set',
+ 'equivalence',
+ 'This is trivial.'
+ ),
+ (
+ 'abelianize',
+ 'left adjoint',
+ 'This functor is left adjoint to the forgetful functor.'
+ ),
+ (
+ 'abelianize',
+ 'preserves finite products',
+ 'TBA.' -- FIXME
+ ),
+ (
+ 'forget_vector',
+ 'right adjoint',
+ 'This functor is right adjoint to the free vector space functor.'
+ ),
+ (
+ 'free_group',
+ 'left adjoint',
+ 'This functor is left adjoint to the forgetful functor.'
+ );
+
+
+INSERT INTO functor_property_assignments
+ (functor_id, property_id, reason, is_satisfied)
+SELECT
+ functor_id, property_id, reason, TRUE
+FROM functor_properties;
+
+DROP TABLE functor_properties;
diff --git a/database/data/023_functor-unsatisfied-properties.sql b/database/data/023_functor-unsatisfied-properties.sql
new file mode 100644
index 00000000..0ea2df90
--- /dev/null
+++ b/database/data/023_functor-unsatisfied-properties.sql
@@ -0,0 +1,36 @@
+CREATE TEMP TABLE functor_non_properties (
+ functor_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ reason TEXT NOT NULL
+);
+
+INSERT INTO functor_non_properties (
+ functor_id,
+ property_id,
+ reason
+)
+VALUES
+ (
+ 'abelianize',
+ 'preserves products',
+ 'TBA.' -- FIXME
+ ),
+ (
+ 'forget_vector',
+ 'preserves initial objects',
+ 'This is trivial.'
+ ),
+ (
+ 'free_group',
+ 'preserves terminal objects',
+ 'This is trivial'
+ );
+
+
+INSERT INTO functor_property_assignments
+ (functor_id, property_id, reason, is_satisfied)
+SELECT
+ functor_id, property_id, reason, FALSE
+FROM functor_non_properties;
+
+DROP TABLE functor_non_properties;
diff --git a/database/migrations/017_functors-table.sql b/database/migrations/017_functors-table.sql
new file mode 100644
index 00000000..21c24e98
--- /dev/null
+++ b/database/migrations/017_functors-table.sql
@@ -0,0 +1,11 @@
+CREATE TABLE functors (
+ id TEXT PRIMARY KEY,
+ name TEXT NOT NULL UNIQUE,
+ source TEXT NOT NULL,
+ target TEXT NOT NULL,
+ description TEXT NOT NULL CHECK (length(description) > 0),
+ url TEXT CHECK (url IS NULL OR url like 'https://%'),
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (source) REFERENCES categories (id) ON DELETE CASCADE,
+ FOREIGN KEY (target) REFERENCES categories (id) ON DELETE CASCADE
+);
\ No newline at end of file
diff --git a/database/migrations/018_functor-properties-tables.sql b/database/migrations/018_functor-properties-tables.sql
new file mode 100644
index 00000000..cbbdb8b0
--- /dev/null
+++ b/database/migrations/018_functor-properties-tables.sql
@@ -0,0 +1,24 @@
+CREATE TABLE functor_properties (
+ id TEXT PRIMARY KEY,
+ prefix TEXT NOT NULL,
+ description TEXT NOT NULL CHECK (length(description) > 0),
+ nlab_link TEXT CHECK (nlab_link IS NULL OR nlab_link like 'https://%'),
+ invariant_under_equivalences INTEGER NOT NULL DEFAULT TRUE,
+ dual_property_id TEXT,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (prefix) REFERENCES prefixes (prefix) ON DELETE RESTRICT,
+ FOREIGN KEY (dual_property_id) REFERENCES functor_properties (id) ON DELETE SET NULL
+);
+
+CREATE TABLE functor_property_assignments (
+ functor_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ is_satisfied INTEGER NOT NULL CHECK (is_satisfied IN (TRUE, FALSE)),
+ reason TEXT NOT NULL CHECK (length(reason) > 0),
+ is_deduced INTEGER NOT NULL DEFAULT FALSE CHECK (is_deduced in (TRUE, FALSE)),
+ position INTEGER NOT NULL DEFAULT 0,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (functor_id, property_id),
+ FOREIGN KEY (functor_id) REFERENCES functors (id) ON DELETE CASCADE,
+ FOREIGN KEY (property_id) REFERENCES functor_properties (id) ON DELETE CASCADE
+);
\ No newline at end of file
diff --git a/database/migrations/019_functor-implications.sql b/database/migrations/019_functor-implications.sql
new file mode 100644
index 00000000..5c6c700c
--- /dev/null
+++ b/database/migrations/019_functor-implications.sql
@@ -0,0 +1,54 @@
+CREATE TABLE functor_implications (
+ id TEXT PRIMARY KEY,
+ reason TEXT NOT NULL CHECK (length(reason) > 0),
+ dualized_from TEXT,
+ is_equivalence INTEGER NOT NULL DEFAULT FALSE,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (dualized_from) REFERENCES functor_implications (id) ON DELETE SET NULL
+);
+
+CREATE TABLE functor_implication_assumptions (
+ implication_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (implication_id, property_id),
+ FOREIGN KEY (implication_id) REFERENCES functor_implications (id) ON DELETE CASCADE,
+ FOREIGN KEY (property_id) REFERENCES functor_properties (id) ON DELETE CASCADE
+);
+
+CREATE TABLE functor_implication_conclusions (
+ implication_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (implication_id, property_id),
+ FOREIGN KEY (implication_id) REFERENCES functor_implications (id) ON DELETE CASCADE,
+ FOREIGN KEY (property_id) REFERENCES functor_properties (id) ON DELETE CASCADE
+);
+
+-- property of source category
+CREATE TABLE functor_implication_source_assumptions (
+ implication_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (implication_id, property_id),
+ FOREIGN KEY (implication_id) REFERENCES functor_implications (id) ON DELETE CASCADE,
+ FOREIGN KEY (property_id) REFERENCES properties (id) ON DELETE CASCADE
+);
+
+-- property of target category
+CREATE TABLE functor_implication_target_assumptions (
+ implication_id TEXT NOT NULL,
+ property_id TEXT NOT NULL,
+ created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ PRIMARY KEY (implication_id, property_id),
+ FOREIGN KEY (implication_id) REFERENCES functor_implications (id) ON DELETE CASCADE,
+ FOREIGN KEY (property_id) REFERENCES properties (id) ON DELETE CASCADE
+);
+
+CREATE INDEX idx_assumptions_functor_property ON functor_implication_assumptions (property_id);
+
+CREATE INDEX idx_conclusions_functor_property ON functor_implication_conclusions (property_id);
+
+CREATE INDEX idx_assumptions_functor_source_property ON functor_implication_source_assumptions (property_id);
+
+CREATE INDEX idx_conclusions_functor_target_property ON functor_implication_target_assumptions (property_id);
\ No newline at end of file
diff --git a/database/migrations/020_functor-implication-view.sql b/database/migrations/020_functor-implication-view.sql
new file mode 100644
index 00000000..76528ce7
--- /dev/null
+++ b/database/migrations/020_functor-implication-view.sql
@@ -0,0 +1,80 @@
+CREATE VIEW functor_implications_view AS
+SELECT
+ i.id,
+ i.is_equivalence,
+ i.reason,
+ i.dualized_from,
+ (
+ SELECT json_group_array(property_id) FROM (
+ SELECT property_id
+ FROM functor_implication_assumptions a
+ WHERE a.implication_id = i.id
+ ORDER BY lower(property_id)
+ )
+ ) AS assumptions,
+ (
+ SELECT json_group_array(property_id) FROM (
+ SELECT property_id
+ FROM functor_implication_source_assumptions a
+ WHERE a.implication_id = i.id
+ ORDER BY lower(property_id)
+ )
+ ) AS source_assumptions,
+ (
+ SELECT json_group_array(property_id) FROM (
+ SELECT property_id
+ FROM functor_implication_target_assumptions a
+ WHERE a.implication_id = i.id
+ ORDER BY lower(property_id)
+ )
+ ) AS target_assumptions,
+ (
+ SELECT json_group_array(property_id) FROM (
+ SELECT property_id
+ FROM functor_implication_conclusions c
+ WHERE c.implication_id = i.id
+ ORDER BY lower(property_id)
+ )
+ ) AS conclusions
+FROM functor_implications i;
+
+
+CREATE VIEW functor_implication_input AS
+SELECT
+ NULL AS id,
+ NULL AS is_equivalence,
+ NULL AS assumptions,
+ NULL AS source_assumptions,
+ NULL AS target_assumptions,
+ NULL AS conclusions,
+ NULL AS reason,
+ NULL AS dualized_from;
+
+
+CREATE TRIGGER trg_functor_implication_input_insert
+INSTEAD OF INSERT ON functor_implication_input
+BEGIN
+ INSERT INTO functor_implications (id, reason, is_equivalence, dualized_from)
+ VALUES (
+ NEW.id,
+ NEW.reason,
+ COALESCE(NEW.is_equivalence, FALSE),
+ NEW.dualized_from
+ );
+
+ INSERT INTO functor_implication_assumptions (implication_id, property_id)
+ SELECT NEW.id, value
+ FROM json_each(NEW.assumptions);
+
+ INSERT INTO functor_implication_source_assumptions (implication_id, property_id)
+ SELECT NEW.id, value
+ FROM json_each(NEW.source_assumptions);
+
+ INSERT INTO functor_implication_target_assumptions (implication_id, property_id)
+ SELECT NEW.id, value
+ FROM json_each(NEW.target_assumptions);
+
+ INSERT INTO functor_implication_conclusions (implication_id, property_id)
+ SELECT NEW.id, value
+ FROM json_each(NEW.conclusions);
+END;
\ No newline at end of file
diff --git a/scripts/deduce-properties.ts b/scripts/deduce-properties.ts
index 9c3d6b46..8a3ccb81 100644
--- a/scripts/deduce-properties.ts
+++ b/scripts/deduce-properties.ts
@@ -101,7 +101,7 @@ async function deduce_satisfied_properties(
const assumption_string = get_assumption_string(implication)
const conclusion_string = get_conclusion_string(implication)
- const ref = `(see here)`
+ const ref = `(see here)`
const reason = `Since it ${assumption_string}, it ${conclusion_string} ${ref}.`
reasons[conclusion] = reason
@@ -221,7 +221,7 @@ async function deduce_unsatisfied_properties(
const assumption_string = get_assumption_string(implication)
const conclusion_string = get_conclusion_string(implication)
- const ref = `(see here)`
+ const ref = `(see here)`
const reason =
`Assume that it ${prefixes[property]} ${property}. ` +
diff --git a/src/app.d.ts b/src/app.d.ts
index 4e661d5d..cc658706 100644
--- a/src/app.d.ts
+++ b/src/app.d.ts
@@ -1,10 +1,10 @@
-// See https://svelte.dev/docs/kit/types#app.d.ts
-// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
- // interface PageData {}
+ interface PageData {
+ mode: 'categories' | 'functors'
+ }
// interface PageState {}
// interface Platform {}
}
diff --git a/src/components/FunctorList.svelte b/src/components/FunctorList.svelte
new file mode 100644
index 00000000..f0228825
--- /dev/null
+++ b/src/components/FunctorList.svelte
@@ -0,0 +1,38 @@
+
+
+{#if description}
+
+ {description}
+
+{/if}
+
+{#if functors.length}
+
+{:else}
+ —
+{/if}
+
+
diff --git a/src/components/Heading.svelte b/src/components/Heading.svelte
index 1873c48d..89d73e57 100644
--- a/src/components/Heading.svelte
+++ b/src/components/Heading.svelte
@@ -1,6 +1,7 @@
-
+
diff --git a/src/components/ModeSelector.svelte b/src/components/ModeSelector.svelte
new file mode 100644
index 00000000..45008597
--- /dev/null
+++ b/src/components/ModeSelector.svelte
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/src/components/Nav.svelte b/src/components/Nav.svelte
index de878889..495a3343 100644
--- a/src/components/Nav.svelte
+++ b/src/components/Nav.svelte
@@ -16,6 +16,7 @@
text: string
nested?: string
icon: IconDefinition
+ mode?: string
}
const links: Link[] = [
@@ -23,19 +24,68 @@
{
href: '/categories',
text: 'Categories',
- nested: '/category',
+ nested: '/category/',
icon: faDatabase,
+ mode: 'categories',
+ },
+ {
+ href: '/category-properties',
+ text: 'Properties',
+ nested: '/category-property',
+ icon: faList,
+ mode: 'categories',
+ },
+ {
+ href: '/category-implications',
+ text: 'Implications',
+ nested: '/category-implication',
+ icon: faArrowsSplitUpAndLeft,
+ mode: 'categories',
+ },
+ {
+ href: '/category-comparison',
+ text: 'Compare',
+ icon: faChartBar,
+ nested: '/category-comparison',
+ mode: 'categories',
+ },
+ {
+ href: '/category-search',
+ text: 'Search',
+ icon: faSearch,
+ mode: 'categories',
+ },
+ {
+ href: '/functors',
+ text: 'Functors',
+ nested: '/functor',
+ icon: faDatabase,
+ mode: 'functors',
+ },
+ {
+ href: '/functor-properties',
+ text: 'Properties',
+ nested: '/functor-property',
+ icon: faList,
+ mode: 'functors',
+ },
+ {
+ href: '/functor-implications',
+ text: 'Implications',
+ nested: '/functor-implication',
+ icon: faArrowsSplitUpAndLeft,
+ mode: 'functors',
},
- { href: '/properties', text: 'Properties', nested: '/property', icon: faList },
- { href: '/implications', text: 'Implications', icon: faArrowsSplitUpAndLeft },
- { href: '/compare', text: 'Compare', icon: faChartBar, nested: '/compare' },
- { href: '/search', text: 'Search', icon: faSearch },
]
+
+ let displayed_links = $derived(
+ links.filter((link) => !link.mode || link.mode === page.data.mode),
+ )