diff --git a/docs/assertions-module/3-nrr.html b/docs/assertions-module/3-nrr.html index 814e0d1d2..b5e6928ab 100644 --- a/docs/assertions-module/3-nrr.html +++ b/docs/assertions-module/3-nrr.html @@ -134,8 +134,8 @@ of subsequent sentences, which also happens in the pre-pass. "this is too long a name for a single relation to have", "and would become unwieldy."); else Node::set_new_relation_here(V->next, - BinaryPredicates::make_pair_sketchily(explicit_bp_family, - WordAssemblages::from_wording(RW), Relation_OtoO)); + Relations::Explicit::make_pair_sketchily( + WordAssemblages::from_wording(RW))); return TRUE; } break; @@ -485,6 +485,8 @@ splitting into cases. kind *storage_kind = NULL; what kind, if any, might be stored in it inference_subject *storage_infs = NULL; summing these up + explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific); + int rvno = FALSE, relate values not objects? dynamic = FALSE, use dynamic memory allocation for storage? provide_prn = FALSE, allocate the storage property to the kind? @@ -497,7 +499,7 @@ splitting into cases. if (rvno) { bp->relates_values_not_objects = TRUE; bpr->relates_values_not_objects = TRUE; } if (RR->frf) { bp->fast_route_finding = TRUE; bpr->fast_route_finding = TRUE; } if (prn) { - bp->i6_storage_property = prn; bpr->i6_storage_property = prn; + ED->i6_storage_property = prn; Properties::Valued::set_stored_relation(prn, bp); } if (dynamic) { @@ -576,8 +578,6 @@ splitting into cases. } } - bpr->form_of_relation = bp->form_of_relation; - LOGIF(RELATION_DEFINITIONS, "Defined the binary predicate:\n$2\n", bp); } @@ -690,7 +690,6 @@ omitted from the index. if ((PK) && (Kinds::Behaviour::is_object(PK) == FALSE)) Properties::Valued::set_kind(prn, PK); if (storage_kind) storage_infs = Kinds::Knowledge::as_subject(storage_kind); else storage_infs = NULL; - if (Kinds::Behaviour::is_object(storage_kind) == FALSE) bp->storage_kind = storage_kind; if (((RR->terms[0].unique) || (RR->terms[1].unique)) && (PK) && (Kinds::Behaviour::is_object(PK) == FALSE)) Properties::Valued::now_used_for_non_typesafe_relation(prn); @@ -726,7 +725,7 @@ and \(14D\) bytes on Glulx, where \(D\) is the size of the domain...

-    bp->form_of_relation = Relation_OtoO;
+    ED->form_of_relation = Relation_OtoO;
     provide_prn = TRUE;
     if (Kinds::Behaviour::is_object(storage_kind)) {
         bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("Relation_Now1to1(*2,%n,*1)", i6_prn_name);
@@ -746,7 +745,7 @@ and \(14D\) bytes on Glulx, where \(D\) is the size of the domain...
 

-    bp->form_of_relation = Relation_OtoV;
+    ED->form_of_relation = Relation_OtoV;
     provide_prn = TRUE;
     if (Kinds::Behaviour::is_object(storage_kind)) {
         bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("*2.%n = *1", i6_prn_name);
@@ -766,7 +765,7 @@ and \(14D\) bytes on Glulx, where \(D\) is the size of the domain...
 

-    bp->form_of_relation = Relation_VtoO;
+    ED->form_of_relation = Relation_VtoO;
     provide_prn = TRUE;
     if (Kinds::Behaviour::is_object(storage_kind)) {
         bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("*1.%n = *2", i6_prn_name);
@@ -787,7 +786,7 @@ various K".
 

-    bp->form_of_relation = Relation_VtoV;
+    ED->form_of_relation = Relation_VtoV;
     BinaryPredicates::mark_as_needed(bp);
     bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(Relation_TestVtoV(*1,%n,*2,false))",
         BinaryPredicates::iname(bp));
@@ -805,7 +804,7 @@ another".
 

-    bp->form_of_relation = Relation_Sym_OtoO;
+    ED->form_of_relation = Relation_Sym_OtoO;
     provide_prn = TRUE;
     if (Kinds::Behaviour::is_object(storage_kind)) {
         bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("Relation_NowS1to1(*2,%n,*1)", i6_prn_name);
@@ -826,7 +825,7 @@ to each other".
 

-    bp->form_of_relation = Relation_Sym_VtoV;
+    ED->form_of_relation = Relation_Sym_VtoV;
     BinaryPredicates::mark_as_needed(bp);
     bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(Relation_TestVtoV(*1,%n,*2,true))",
         BinaryPredicates::iname(bp));
@@ -844,10 +843,10 @@ other in groups".
 

-    bp->form_of_relation = Relation_Equiv;
+    ED->form_of_relation = Relation_Equiv;
     equivalence_bp_data *D = CREATE(equivalence_bp_data);
     D->equivalence_partition = NULL;
-    bp->family_specific = STORE_POINTER_equivalence_bp_data(D);
+    ED->equiv_data = D;
     provide_prn = TRUE;
     if (Kinds::Behaviour::is_object(storage_kind)) {
         bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(*1.%n == *2.%n)", i6_prn_name, i6_prn_name);
@@ -865,15 +864,16 @@ other in groups".
     Properties::Valued::set_kind(prn, K_number);
 
  • This code is used in §5.
-

§5.11. The Relation_ByRoutine case, or relation tested by a routine: "R relates -K to L when (some condition)". +

§5.11. The case of a relation tested by a routine: "R relates K to L when (some +condition)".

Complete as a relation-by-routine BP5.11 =

-    bp->form_of_relation = Relation_ByRoutine;
+    bp->relation_family = by_routine_bp_family;
+    bp->reversal->relation_family = by_routine_bp_family;
     package_request *P = BinaryPredicates::package(bp);
     by_routine_bp_data *D = CREATE(by_routine_bp_data);
     D->condition_defn_text = RR->CONW;
diff --git a/docs/assertions-module/5-er.html b/docs/assertions-module/5-er.html
index 3b3e94b5d..265db3b51 100644
--- a/docs/assertions-module/5-er.html
+++ b/docs/assertions-module/5-er.html
@@ -95,75 +95,160 @@ there are none.
 
 
 bp_family *explicit_bp_family = NULL;
+bp_family *by_routine_bp_family = NULL;
 typedef struct explicit_bp_data {
+    int form_of_relation;  one of the Relation_* constants defined below
+    struct property *i6_storage_property;  provides run-time storage
+    struct equivalence_bp_data *equiv_data;  only used for Relation_Equiv
+    struct inter_name *v2v_bitmap_iname;  only used for Relation_VtoV and Relation_Sym_VtoV
     CLASS_DEFINITION
 } explicit_bp_data;
 
-
  • The structure explicit_bp_data is private to this section.
+
  • The structure explicit_bp_data is accessed in 3/nrr and here.

§2.

 void Relations::Explicit::start(void) {
     explicit_bp_family = BinaryPredicateFamilies::new();
-    METHOD_ADD(explicit_bp_family, TYPECHECK_BPF_MTID, Relations::Explicit::REL_typecheck);
-    METHOD_ADD(explicit_bp_family, ASSERT_BPF_MTID, Relations::Explicit::REL_assert);
-    METHOD_ADD(explicit_bp_family, SCHEMA_BPF_MTID, Relations::Explicit::REL_compile);
-    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, Relations::Explicit::REL_describe_for_problems);
-    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, Relations::Explicit::REL_describe_briefly);
+    METHOD_ADD(explicit_bp_family, TYPECHECK_BPF_MTID, Relations::Explicit::REL_typecheck);
+    METHOD_ADD(explicit_bp_family, ASSERT_BPF_MTID, Relations::Explicit::REL_assert);
+    METHOD_ADD(explicit_bp_family, SCHEMA_BPF_MTID, Relations::Explicit::REL_compile);
+    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, Relations::Explicit::REL_describe_for_problems);
+    METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, Relations::Explicit::REL_describe_briefly);
+    by_routine_bp_family = BinaryPredicateFamilies::new();
+    METHOD_ADD(by_routine_bp_family, TYPECHECK_BPF_MTID, Relations::Explicit::REL_typecheck);
+    METHOD_ADD(by_routine_bp_family, ASSERT_BPF_MTID, Relations::Explicit::REL_assert);
+    METHOD_ADD(by_routine_bp_family, SCHEMA_BPF_MTID, Relations::Explicit::REL_compile);
+    METHOD_ADD(by_routine_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, Relations::Explicit::REL_describe_for_problems);
+    METHOD_ADD(by_routine_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, Relations::Explicit::REL_br_describe_briefly);
+}
+
+int Relations::Explicit::is_explicit_with_runtime_storage(binary_predicate *bp) {
+    if (bp->relation_family == explicit_bp_family) return TRUE;
+    return TRUE;
 }
 
-

§3. They typecheck by the default rule only: +

§3. The following constants are numbered in a way which corresponds to some +run-time code supporting relations. +

+ +
define Relation_Implicit	-1  used to mean "none of the below"
+define Relation_OtoO		1  one to one: "R relates one K to one K"
+define Relation_OtoV		2  one to various: "R relates one K to various K"
+define Relation_VtoO		3  various to one: "R relates various K to one K"
+define Relation_VtoV		4  various to various: "R relates various K to various K"
+define Relation_Sym_OtoO	5  symmetric one to one: "R relates one K to another"
+define Relation_Sym_VtoV	6  symmetric various to various: "R relates K to each other"
+define Relation_Equiv		7  equivalence relation: "R relates K to each other in groups"
+
+

§4.

+ +
+int Relations::Explicit::allow_arbitrary_assertions(binary_predicate *bp) {
+    int f = Relations::Explicit::get_form_of_relation(bp);
+    if (f == Relation_Equiv) return TRUE;
+    if (f == Relation_VtoV) return TRUE;
+    if (f == Relation_Sym_VtoV) return TRUE;
+    return FALSE;
+}
+
+

§5. When the source text declares new relations, it turns out to be convenient +to make their BPs in a two-stage process: to make sketchy, mostly-blank BP +structures for them early on — but getting their names registered — and +then fill in the correct details later. This is where such sketchy pairs are +made:

-int Relations::Explicit::REL_typecheck(bp_family *self, binary_predicate *bp,
+binary_predicate *Relations::Explicit::make_pair_sketchily(word_assemblage wa) {
+    TEMPORARY_TEXT(relname)
+    WRITE_TO(relname, "%V", WordAssemblages::first_word(&wa));
+    binary_predicate *bp =
+        BinaryPredicates::make_pair(explicit_bp_family,
+        BPTerms::new(NULL), BPTerms::new(NULL),
+        relname, NULL, NULL, NULL, wa);
+    DISCARD_TEXT(relname)
+    explicit_bp_data *ED = CREATE(explicit_bp_data);
+    bp->family_specific = STORE_POINTER_explicit_bp_data(ED);
+    bp->reversal->family_specific = STORE_POINTER_explicit_bp_data(ED);
+
+    ED->equiv_data = NULL;
+    ED->i6_storage_property = NULL;
+    ED->form_of_relation = Relation_OtoO;
+    ED->v2v_bitmap_iname = NULL;
+
+    return bp;
+}
+
+property *Relations::Explicit::get_i6_storage_property(binary_predicate *bp) {
+    if (bp->relation_family != explicit_bp_family) return NULL;
+    explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+    return ED->i6_storage_property;
+}
+
+int Relations::Explicit::get_form_of_relation(binary_predicate *bp) {
+    if (bp->relation_family != explicit_bp_family) return Relation_Implicit;
+    explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+    return ED->form_of_relation;
+}
+char *Relations::Explicit::form_to_text(binary_predicate *bp) {
+    switch(Relations::Explicit::get_form_of_relation(bp)) {
+        case Relation_OtoO: return "Relation_OtoO";
+        case Relation_OtoV: return "Relation_OtoV";
+        case Relation_VtoO: return "Relation_VtoO";
+        case Relation_VtoV: return "Relation_VtoV";
+        case Relation_Sym_OtoO: return "Relation_Sym_OtoO";
+        case Relation_Sym_VtoV: return "Relation_Sym_VtoV";
+        case Relation_Equiv: return "Relation_Equiv";
+        default: return "Relation_Implicit";
+    }
+}
+
+

§6. They typecheck by the default rule only: +

+ +
+int Relations::Explicit::REL_typecheck(bp_family *self, binary_predicate *bp,
         kind **kinds_of_terms, kind **kinds_required, tc_problem_kit *tck) {
     return DECLINE_TO_MATCH;
 }
 
-

§4. They are asserted thus. Note that if we have a symmetric relation then we need +

§7. They are asserted thus. Note that if we have a symmetric relation then we need to behave as if \(B(y, x)\) had also been asserted whenever \(B(x, y)\) has, if \(x\neq y\).

-int Relations::Explicit::REL_assert(bp_family *self, binary_predicate *bp,
+int Relations::Explicit::REL_assert(bp_family *self, binary_predicate *bp,
         inference_subject *infs0, parse_node *spec0,
         inference_subject *infs1, parse_node *spec1) {
 
-    Reject non-assertable relations4.1;
+    Reject non-assertable relations7.1;
     if (BinaryPredicates::store_dynamically(bp)) {
         World::Inferences::draw_relation_spec(bp, spec0, spec1);
         return TRUE;
     } else {
-        if ((infs0 == NULL) || (infs1 == NULL)) Reject relationship with nothing4.2;
+        if ((infs0 == NULL) || (infs1 == NULL)) Reject relationship with nothing7.2;
         if (Relations::Explicit::allow_arbitrary_assertions(bp)) {
             World::Inferences::draw_relation(bp, infs0, infs1);
-            if ((BinaryPredicates::get_form_of_relation(bp) == Relation_Sym_VtoV) && (infs0 != infs1))
+            if ((Relations::Explicit::get_form_of_relation(bp) == Relation_Sym_VtoV) && (infs0 != infs1))
                 World::Inferences::draw_relation(bp, infs1, infs0);
             return TRUE;
         }
-        if (BinaryPredicates::is_explicit_with_runtime_storage(bp)) {
-            Relations::Explicit::infer_property_based_relation(bp, infs1, infs0);
-            if ((BinaryPredicates::get_form_of_relation(bp) == Relation_Sym_OtoO) && (infs0 != infs1))
-                Relations::Explicit::infer_property_based_relation(bp, infs0, infs1);
+        if (Relations::Explicit::is_explicit_with_runtime_storage(bp)) {
+            Relations::Explicit::infer_property_based_relation(bp, infs1, infs0);
+            if ((Relations::Explicit::get_form_of_relation(bp) == Relation_Sym_OtoO) && (infs0 != infs1))
+                Relations::Explicit::infer_property_based_relation(bp, infs0, infs1);
             return TRUE;
         }
     }
     return FALSE;
 }
-
-int Relations::Explicit::allow_arbitrary_assertions(binary_predicate *bp) {
-    if (bp->form_of_relation == Relation_Equiv) return TRUE;
-    if (bp->form_of_relation == Relation_VtoV) return TRUE;
-    if (bp->form_of_relation == Relation_Sym_VtoV) return TRUE;
-    return FALSE;
-}
 
-

§4.1. This is the point at which non-assertable relations are thrown out. +

§7.1. This is the point at which non-assertable relations are thrown out.

-

Reject non-assertable relations4.1 = +

Reject non-assertable relations7.1 =

@@ -177,8 +262,8 @@ to behave as if \(B(y, x)\) had also been asserted whenever \(B(x, y)\) has, if
         return TRUE;
     }
 
-
  • This code is used in §4.
-

§4.2. Reject relationship with nothing4.2 = +

  • This code is used in §7.
+

§7.2. Reject relationship with nothing7.2 =

@@ -190,8 +275,8 @@ to behave as if \(B(y, x)\) had also been asserted whenever \(B(x, y)\) has, if
         "to try to put 'Mr Cogito' and 'nothing' into a relationship.");
     return TRUE;
 
-
  • This code is used in §4.
-

§5. This routine converts the knowledge that \(R(ox, oy)\) into a single +

  • This code is used in §7.
+

§8. This routine converts the knowledge that \(R(ox, oy)\) into a single inference. It can only be used for a simple subclass of the relations: those which store oy, the only thing related to ox, in a given property of ox. The beauty of this is that the "only thing related to" business @@ -201,32 +286,32 @@ inferences for \(y\) and \(z\).

-void Relations::Explicit::infer_property_based_relation(binary_predicate *relation,
+void Relations::Explicit::infer_property_based_relation(binary_predicate *bp,
     inference_subject *infs0, inference_subject *infs1) {
-    if (BinaryPredicates::get_form_of_relation(relation) == Relation_VtoO) {
+    if (Relations::Explicit::get_form_of_relation(bp) == Relation_VtoO) {
         inference_subject *swap=infs0; infs0=infs1; infs1=swap;
     }
-    property *prn = BinaryPredicates::get_i6_storage_property(relation);
+    property *prn = Relations::Explicit::get_i6_storage_property(bp);
     World::Inferences::draw_property(infs0, prn, InferenceSubjects::as_constant(infs1));
 }
 
-

§6. We need do nothing special: these relations can be compiled from their schemas. +

§9. We need do nothing special: these relations can be compiled from their schemas.

-int Relations::Explicit::REL_compile(bp_family *self, int task, binary_predicate *bp, annotated_i6_schema *asch) {
+int Relations::Explicit::REL_compile(bp_family *self, int task, binary_predicate *bp, annotated_i6_schema *asch) {
     return FALSE;
 }
 
-

§7. Problem message text: +

§10. Problem message text:

-int Relations::Explicit::REL_describe_for_problems(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
+int Relations::Explicit::REL_describe_for_problems(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
     return FALSE;
 }
-void Relations::Explicit::REL_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
-    switch (bp->form_of_relation) {
+void Relations::Explicit::REL_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
+    switch (Relations::Explicit::get_form_of_relation(bp)) {
         case Relation_OtoO: WRITE("one-to-one"); break;
         case Relation_OtoV: WRITE("one-to-various"); break;
         case Relation_VtoO: WRITE("various-to-one"); break;
@@ -234,9 +319,11 @@ inferences for \(y\) and \(z\).
         case Relation_Sym_OtoO: WRITE("one-to-another"); break;
         case Relation_Sym_VtoV: WRITE("various-to-each-other"); break;
         case Relation_Equiv: WRITE("in groups"); break;
-        case Relation_ByRoutine: WRITE("defined"); break;
     }
 }
+void Relations::Explicit::REL_br_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
+    WRITE("defined");
+}
 
-

§20.

+

§18.

 text_stream *BinaryPredicates::get_log_name(binary_predicate *bp) {
     return bp->debugging_log_name;
 }
 
-

§21. Miscellaneous access routines.

+

§19. Miscellaneous access routines.

-int BinaryPredicates::get_form_of_relation(binary_predicate *bp) {
-    return bp->form_of_relation;
-}
-int BinaryPredicates::is_explicit_with_runtime_storage(binary_predicate *bp) {
-    if (bp->right_way_round == FALSE) bp = bp->reversal;
-    if (bp->form_of_relation == Relation_Implicit) return FALSE;
-    if (bp->form_of_relation == Relation_ByRoutine) return FALSE;
-    return TRUE;
-}
-char *BinaryPredicates::form_to_text(binary_predicate *bp) {
-    switch(bp->form_of_relation) {
-        case Relation_Implicit: return "Relation_Implicit";
-        case Relation_OtoO: return "Relation_OtoO";
-        case Relation_OtoV: return "Relation_OtoV";
-        case Relation_VtoO: return "Relation_VtoO";
-        case Relation_VtoV: return "Relation_VtoV";
-        case Relation_Sym_OtoO: return "Relation_Sym_OtoO";
-        case Relation_Sym_VtoV: return "Relation_Sym_VtoV";
-        case Relation_Equiv: return "Relation_Equiv";
-        case Relation_ByRoutine: return "Relation_ByRoutine";
-        default: return "formless-BP";
-    }
-}
-
 parse_node *BinaryPredicates::get_bp_created_at(binary_predicate *bp) {
     return bp->bp_created_at;
 }
 
-

§22. Details of the terms: +

§20. Details of the terms:

-kind *BinaryPredicates::term_kind(binary_predicate *bp, int t) {
+kind *BinaryPredicates::term_kind(binary_predicate *bp, int t) {
     if (bp == NULL) internal_error("tried to find kind of null relation");
     return BPTerms::kind(&(bp->term_details[t]));
 }
-i6_schema *BinaryPredicates::get_term_as_fn_of_other(binary_predicate *bp, int t) {
+i6_schema *BinaryPredicates::get_term_as_fn_of_other(binary_predicate *bp, int t) {
     if (bp == NULL) internal_error("tried to find function of null relation");
     return bp->term_details[t].function_of_other;
 }
 
-

§23. Reversing: +

§21. Reversing:

-binary_predicate *BinaryPredicates::get_reversal(binary_predicate *bp) {
+binary_predicate *BinaryPredicates::get_reversal(binary_predicate *bp) {
     if (bp == NULL) internal_error("tried to find reversal of null relation");
     return bp->reversal;
 }
-int BinaryPredicates::is_the_wrong_way_round(binary_predicate *bp) {
+int BinaryPredicates::is_the_wrong_way_round(binary_predicate *bp) {
     if ((bp) && (bp->right_way_round == FALSE)) return TRUE;
     return FALSE;
 }
 
-

§24. For compiling code from conditions: +

§22. For compiling code from conditions:

@@ -610,7 +537,7 @@ so the fact that it runs relatively slowly does not matter.
     return FALSE;
 }
 
-

§25. For the A-parser. The real code is all elsewhere; note that the +

§23. For the A-parser. The real code is all elsewhere; note that the assertions field, which is used only for relations between values rather than objects, is a linked list. (Information about objects is stored in linked lists pointed to from the instance structure in question; that @@ -629,23 +556,18 @@ relation itself.) return bp->knowledge_about_bp; }

-

§26. For use when optimising code. +

§24. For use when optimising code.

-#ifdef CORE_MODULE
-property *BinaryPredicates::get_i6_storage_property(binary_predicate *bp) {
-    return bp->i6_storage_property;
-}
-#endif
-int BinaryPredicates::allows_function_simplification(binary_predicate *bp) {
+int BinaryPredicates::allows_function_simplification(binary_predicate *bp) {
     return bp->allow_function_simplification;
 }
 #ifdef CORE_MODULE
 inter_name *default_rr = NULL;
 void BinaryPredicates::mark_as_needed(binary_predicate *bp) {
     if (bp->record_needed == FALSE) {
-        bp->bp_iname = Hierarchy::make_iname_in(RELATION_RECORD_HL, BinaryPredicates::package(bp));
+        bp->bp_iname = Hierarchy::make_iname_in(RELATION_RECORD_HL, BinaryPredicates::package(bp));
         if (default_rr == NULL) {
             default_rr = bp->bp_iname;
             inter_name *iname = Hierarchy::find(MEANINGLESS_RR_HL);
@@ -664,7 +586,7 @@ relation itself.)
 }
 #endif
 
-

§27. The predicate-calculus engine compiles much better loops if +

§25. The predicate-calculus engine compiles much better loops if we can help it by providing an I6 schema of a loop header solving the following problem:

@@ -681,18 +603,18 @@ of looping over all \(x\) in the left domain of \(R\) and testing every possible
 int BinaryPredicates::write_optimised_loop_schema(i6_schema *sch, binary_predicate *bp) {
     if (bp == NULL) return FALSE;
-    Try loop ranger optimisation27.1;
-    Try loop parent optimisation subject to a proviso27.2;
+    Try loop ranger optimisation25.1;
+    Try loop parent optimisation subject to a proviso25.2;
     return FALSE;
 }
 
-

§27.1. Some relations \(R\) provide a "ranger" routine, R, which is such that +

§25.1. Some relations \(R\) provide a "ranger" routine, R, which is such that R(t) supplies the first "child" of \(t\) and R(t, n) supplies the next "child" after \(n\). Thus R iterates through some linked list of all the objects \(x\) such that \(R(x, t)\).

-

Try loop ranger optimisation27.1 = +

Try loop ranger optimisation25.1 =

@@ -704,8 +626,8 @@ objects \(x\) such that \(R(x, t)\).
         return TRUE;
     }
 
-
  • This code is used in §27.
-

§27.2. Other relations make use of the I6 object tree, in cases where \(R(x, t)\) +

  • This code is used in §25.
+

§25.2. Other relations make use of the I6 object tree, in cases where \(R(x, t)\) is true if and only if \(t\) is an object which is the parent of \(x\) in the I6 object tree and some routine associated with \(R\), called its proviso P, is such that P(x) == t. For example, \({\it worn-by}(x, t)\) @@ -714,7 +636,7 @@ ensures that we don't falsely pick up, say, items carried by \(t\) which aren't being worn, or aren't even clothing.

-

Try loop parent optimisation subject to a proviso27.2 = +

Try loop parent optimisation subject to a proviso25.2 =

@@ -725,7 +647,7 @@ aren't being worn, or aren't even clothing.
         return TRUE;
     }
 
-
  • This code is used in §27.
+
  • This code is used in §25.
diff --git a/docs/calculus-module/3-bpf.html b/docs/calculus-module/3-bpf.html index b3c7ed0a7..4f4ee770f 100644 --- a/docs/calculus-module/3-bpf.html +++ b/docs/calculus-module/3-bpf.html @@ -231,8 +231,8 @@ fields, when they are mentioned in problem messages. else WRITE("a"); WRITE(" relation"); } - kind *K0 = BinaryPredicates::term_kind(bp, 0); if (K0 == NULL) K0 = K_object; - kind *K1 = BinaryPredicates::term_kind(bp, 1); if (K1 == NULL) K1 = K_object; + kind *K0 = BinaryPredicates::term_kind(bp, 0); if (K0 == NULL) K0 = K_object; + kind *K1 = BinaryPredicates::term_kind(bp, 1); if (K1 == NULL) K1 = K_object; WRITE(" (between "); if (Kinds::eq(K0, K1)) { Kinds::Textual::write_plural(OUT, K0); diff --git a/docs/calculus-module/3-bptd.html b/docs/calculus-module/3-bptd.html index 7efd42266..746952824 100644 --- a/docs/calculus-module/3-bptd.html +++ b/docs/calculus-module/3-bptd.html @@ -128,7 +128,7 @@ the ideas are exactly the same.

§2.

-bp_term_details BPTerms::new(TERM_DOMAIN_CALCULUS_TYPE *infs) {
+bp_term_details BPTerms::new(TERM_DOMAIN_CALCULUS_TYPE *infs) {
     bp_term_details bptd;
     bptd.called_name = EMPTY_WORDING;
     bptd.function_of_other = NULL;
@@ -228,7 +228,7 @@ container (if any) is at present directly containing \(y\).
 

-kind *BPTerms::kind(bp_term_details *bptd) {
+kind *BPTerms::kind(bp_term_details *bptd) {
     if (bptd == NULL) return NULL;
     if (bptd->implies_kind) return bptd->implies_kind;
     return TERM_DOMAIN_TO_KIND_FUNCTION(bptd->implies_infs);
diff --git a/docs/calculus-module/3-ter.html b/docs/calculus-module/3-ter.html
index 333d079bd..f4949fafb 100644
--- a/docs/calculus-module/3-ter.html
+++ b/docs/calculus-module/3-ter.html
@@ -113,16 +113,16 @@ since (alone among binary predicates) it has no distinct reversal.
 
 void Calculus::Equality::stock(bp_family *self, int n) {
     if (n == 1) {
-        R_equality = BinaryPredicates::make_equality(equality_bp_family,
+        R_equality = BinaryPredicates::make_equality(equality_bp_family,
             PreformUtilities::wording(<relation-names>, EQUALITY_RELATION_NAME));
-        BinaryPredicates::set_index_details(R_equality, "value", "value");
+        BinaryPredicates::set_index_details(R_equality, "value", "value");
     }
 }
 
 void Calculus::Equality::stock_spatial(bp_family *self, int n) {
     if (n == 1) {
         a_has_b_predicate =
-            BinaryPredicates::make_pair(spatial_bp_family,
+            BinaryPredicates::make_pair(spatial_bp_family,
                 BPTerms::new_full(NULL, NULL, EMPTY_WORDING, NULL),
                 BPTerms::new(NULL),
                 I"has", I"is-had-by",
diff --git a/docs/calculus-module/4-ap.html b/docs/calculus-module/4-ap.html
index 9a2ce2074..932c71d38 100644
--- a/docs/calculus-module/4-ap.html
+++ b/docs/calculus-module/4-ap.html
@@ -403,7 +403,7 @@ quantifier is:
 
 
     binary_predicate *bp = RETRIEVE_POINTER_binary_predicate(prop->predicate);
-    if (bp == NULL) WRITE("?bad-bp?"); else WRITE("%S", BinaryPredicates::get_log_name(bp));
+    if (bp == NULL) WRITE("?bad-bp?"); else WRITE("%S", BinaryPredicates::get_log_name(bp));
 
  • This code is used in §14.

§14.4. Log a comma-separated list of terms for this atomic proposition14.4 = diff --git a/docs/calculus-module/4-tcp.html b/docs/calculus-module/4-tcp.html index 631f38821..3d9d147ea 100644 --- a/docs/calculus-module/4-tcp.html +++ b/docs/calculus-module/4-tcp.html @@ -359,7 +359,7 @@ would work instead. If it would, we make the change within the proposition.

     binary_predicate *bp = RETRIEVE_POINTER_binary_predicate(pl->predicate);
-    if (BinaryPredicates::is_the_wrong_way_round(bp)) internal_error("BP wrong way round");
+    if (BinaryPredicates::is_the_wrong_way_round(bp)) internal_error("BP wrong way round");
     if (Propositions::Checker::type_check_binary_predicate(pl, &vta, tck) == NEVER_MATCH) {
         if (bp == R_equality) {
             unary_predicate *alt = Terms::noun_to_adj_conversion(pl->terms[1]);
@@ -461,7 +461,7 @@ function.
 
 
 kind *Propositions::Checker::approximate_argument_kind(binary_predicate *bp, int i) {
-    kind *K = BinaryPredicates::term_kind(bp, i);
+    kind *K = BinaryPredicates::term_kind(bp, i);
     if (K == NULL) return K_object;
     return Kinds::weaken(K, K_object);
 }
@@ -545,7 +545,7 @@ produce a kinds_required
     for (int i=0; i<2; i++) {
-        kind *K = Kinds::weaken(BinaryPredicates::term_kind(bp, i), K_object);
+        kind *K = Kinds::weaken(BinaryPredicates::term_kind(bp, i), K_object);
         if (K == NULL) K = K_object;
         kinds_required[i] = K;
     }
diff --git a/docs/calculus-module/4-trm.html b/docs/calculus-module/4-trm.html
index 770276603..782a23796 100644
--- a/docs/calculus-module/4-trm.html
+++ b/docs/calculus-module/4-trm.html
@@ -344,7 +344,7 @@ value, because this might not yet have been checked otherwise.
     }
     if (pt.function) {
         binary_predicate *bp = (pt.function)->bp;
-        i6_schema *fn = BinaryPredicates::get_term_as_fn_of_other(bp, 1-pt.function->from_term);
+        i6_schema *fn = BinaryPredicates::get_term_as_fn_of_other(bp, 1-pt.function->from_term);
         if (fn == NULL) internal_error("function of non-functional predicate");
         Calculus::Schemas::emit_expand_from_terms(fn, &(pt.function->fn_of), NULL, FALSE);
         return;
@@ -378,7 +378,7 @@ concisely and without fuss.
         Node::log_node(OUT, C);
     } else if (pt->function) {
         binary_predicate *bp = pt->function->bp;
-        i6_schema *fn = BinaryPredicates::get_term_as_fn_of_other(bp, 1-pt->function->from_term);
+        i6_schema *fn = BinaryPredicates::get_term_as_fn_of_other(bp, 1-pt->function->from_term);
         if (fn == NULL) internal_error("function of non-functional predicate");
         Calculus::Schemas::write_applied(OUT, fn, &(pt->function->fn_of));
     } else if (pt->variable >= 0) {
diff --git a/docs/calculus-module/5-cs.html b/docs/calculus-module/5-cs.html
index b7796a43f..c119e6aba 100644
--- a/docs/calculus-module/5-cs.html
+++ b/docs/calculus-module/5-cs.html
@@ -167,7 +167,7 @@ error.
 

-void Calculus::Schemas::modify(i6_schema *sch, char *fmt, ...) {
+void Calculus::Schemas::modify(i6_schema *sch, char *fmt, ...) {
     va_list ap;  the variable argument list signified by the dots
     sch->prototype = Streams::new_buffer(TYPICAL_I6_SCHEMA_LENGTH, sch->prototype_storage);
     sch->no_quoted_inames = 0;
diff --git a/docs/calculus-module/5-sc.html b/docs/calculus-module/5-sc.html
index 6e0a522eb..e3971253b 100644
--- a/docs/calculus-module/5-sc.html
+++ b/docs/calculus-module/5-sc.html
@@ -317,13 +317,13 @@ we apply it independently to the SP and OP:
 

-    kind *subject_K = BinaryPredicates::term_kind(verb_phrase_relation, 0);
+    kind *subject_K = BinaryPredicates::term_kind(verb_phrase_relation, 0);
     if (Kinds::Behaviour::is_subkind_of_object(subject_K)) subject_K = NULL;
     subject_phrase_prop =
         Propositions::FromSentences::NP_subtree_to_proposition(&subject_phrase_term, subject_phrase_subtree,
             subject_K);
 
-    kind *object_K = BinaryPredicates::term_kind(verb_phrase_relation, 1);
+    kind *object_K = BinaryPredicates::term_kind(verb_phrase_relation, 1);
     if (Kinds::Behaviour::is_subkind_of_object(object_K)) object_K = NULL;
     object_phrase_prop =
         Propositions::FromSentences::NP_subtree_to_proposition(&object_phrase_term, object_phrase_subtree,
@@ -515,7 +515,7 @@ is irregular because it differs from "something" and "someone".)
     if ((k_atom) && (Kinds::eq(KindPredicates::get_kind(k_atom), K_room)) &&
         (verb_phrase_relation == R_equality) && (room_containment_predicate)) {
         KindPredicates::set_composited(k_atom, FALSE);
-        verb_phrase_relation = BinaryPredicates::get_reversal(room_containment_predicate);
+        verb_phrase_relation = BinaryPredicates::get_reversal(room_containment_predicate);
         LOGIF(PREDICATE_CALCULUS, "[%d] Decompositing object: $D\n",
             conv_log_depth, object_phrase_prop);
     }
diff --git a/docs/calculus-module/5-smp.html b/docs/calculus-module/5-smp.html
index b6071117e..a6328861b 100644
--- a/docs/calculus-module/5-smp.html
+++ b/docs/calculus-module/5-smp.html
@@ -206,9 +206,9 @@ the view that a double negative is a positive.
     if (i == 1) position = pl_prev;  ...but up close to the predicate if it's the OP
      insert four atoms, in reverse order, at this position:
     prop = Propositions::insert_atom(prop, position, Atoms::new(DOMAIN_CLOSE_ATOM));
-    if (BinaryPredicates::term_kind(bp, i))
+    if (BinaryPredicates::term_kind(bp, i))
         prop = Propositions::insert_atom(prop, position,
-            KindPredicates::new_atom(BinaryPredicates::term_kind(bp, i), new_var));
+            KindPredicates::new_atom(BinaryPredicates::term_kind(bp, i), new_var));
     prop = Propositions::insert_atom(prop, position, Atoms::new(DOMAIN_OPEN_ATOM));
     prop = Propositions::insert_atom(prop, position, Atoms::QUANTIFIER_new(not_exists_quantifier, nv, 0));
 
@@ -477,7 +477,7 @@ because the code testing them is written to cope properly with bogus values. for (i=1; i>=0; i--) { int v = pl->terms[i].variable; if (v >= 0) { - kind *K = BinaryPredicates::term_kind(bp, i); + kind *K = BinaryPredicates::term_kind(bp, i); if (K) { pcalc_prop *new_KIND = KindPredicates::new_atom(K, Terms::new_variable(v)); new_KIND->saved_bp = bp; @@ -834,11 +834,11 @@ would matter if they were.) TRAVERSE_PROPOSITION(pl, prop) { binary_predicate *bp = Atoms::is_binary_predicate(pl); - if ((bp) && (BinaryPredicates::is_the_wrong_way_round(bp))) { + if ((bp) && (BinaryPredicates::is_the_wrong_way_round(bp))) { pcalc_term pt = pl->terms[0]; pl->terms[0] = pl->terms[1]; pl->terms[1] = pt; - pl->predicate = STORE_POINTER_binary_predicate(BinaryPredicates::get_reversal(bp)); + pl->predicate = STORE_POINTER_binary_predicate(BinaryPredicates::get_reversal(bp)); PROPOSITION_EDITED(pl, prop); } } @@ -935,8 +935,8 @@ why it was necessary to separate the two out. binary_predicate *bp = Atoms::is_binary_predicate(pl); if (bp) for (int j=0; j<2; j++) - if ((BinaryPredicates::get_term_as_fn_of_other(bp, j)) && - (BinaryPredicates::allows_function_simplification(bp))) { + if ((BinaryPredicates::get_term_as_fn_of_other(bp, j)) && + (BinaryPredicates::allows_function_simplification(bp))) { pl->terms[1-j] = Terms::new_function(bp, pl->terms[1-j], 1-j); pl->predicate = STORE_POINTER_binary_predicate(R_equality); PROPOSITION_EDITED(pl, prop); @@ -1119,7 +1119,7 @@ they have no "not a valid case" value analogous to the non-object binary_predicate *bp = NULL; pcalc_term KIND_term = kind_atom->terms[0]; if (KIND_term.function) bp = KIND_term.function->bp; - if ((bp) && (Kinds::eq(K, BinaryPredicates::term_kind(bp, 1)))) { + if ((bp) && (Kinds::eq(K, BinaryPredicates::term_kind(bp, 1)))) { prop = Propositions::ungroup_after(prop, pl_prev, NULL); remove negation grouping prop = Propositions::delete_atom(prop, pl_prev); remove kind=K now insert equality predicate: diff --git a/docs/calculus-module/P-wtmd.html b/docs/calculus-module/P-wtmd.html index 83f262cc6..9946d3173 100644 --- a/docs/calculus-module/P-wtmd.html +++ b/docs/calculus-module/P-wtmd.html @@ -269,12 +269,12 @@ the containment relation and the wearing relation. To avoid scaring the horses, binary predicates are called "relations" in all of the Inform documentation.

-

New BPs can be constructed with BinaryPredicates::make_pair. The term "pair" +

New BPs can be constructed with BinaryPredicates::make_pair. The term "pair" is used because every \(B\) has a "reversal" \(B^r\), such that \(B^r(s, t)\) is true if and only if \(B(t, s)\). \(B\) and \(B^r\) are created in pairs.7

-
-

§3.

+

§4.

-void RTRelations::compile_relation_records(void) {
+void RTRelations::compile_relation_records(void) {
     binary_predicate *bp;
     LOOP_OVER(bp, binary_predicate) {
         binary_predicate *dbp = bp;
@@ -162,8 +173,8 @@ combinations.
         if (bp->record_needed) {
             inter_name *handler = NULL;
             if (bp->dynamic_memory == FALSE)
-                Write the relation handler routine for this BP3.2;
-            Write the relation record for this BP3.1;
+                Write the relation handler routine for this BP4.2;
+            Write the relation record for this BP4.1;
         }
     }
     inter_name *iname = Hierarchy::find(CREATEDYNAMICRELATIONS_HL);
@@ -188,7 +199,7 @@ combinations.
                 DISCARD_TEXT(A)
             Produce::up(Emit::tree());
 
-            switch(bp->form_of_relation) {
+            switch(Relations::Explicit::get_form_of_relation(bp)) {
                 case Relation_OtoO:
                     Produce::inv_call_iname(Emit::tree(), Hierarchy::find(RELATION_TY_OTOOADJECTIVE_HL));
                     Produce::down(Emit::tree());
@@ -248,7 +259,7 @@ combinations.
     Hierarchy::make_available(Emit::tree(), iname);
 }
 
-

§3.1. Write the relation record for this BP3.1 = +

§4.1. Write the relation record for this BP4.1 =

@@ -260,17 +271,17 @@ combinations.
         Kinds::RunTime::emit_block_value_header(BinaryPredicates::kind(bp), FALSE, 8);
         Emit::array_null_entry();
         Emit::array_null_entry();
-        Write the name field of the relation record3.1.1;
-        Write the permissions field of the relation record3.1.2;
-        Write the storage field of the relation metadata array3.1.3;
-        Write the kind field of the relation record3.1.4;
-        Write the handler field of the relation record3.1.6;
-        Write the description field of the relation record3.1.5;
+        Write the name field of the relation record4.1.1;
+        Write the permissions field of the relation record4.1.2;
+        Write the storage field of the relation metadata array4.1.3;
+        Write the kind field of the relation record4.1.4;
+        Write the handler field of the relation record4.1.6;
+        Write the description field of the relation record4.1.5;
     }
     Emit::array_end(save);
 
-
  • This code is used in §3.
-

§3.1.1. Write the name field of the relation record3.1.1 = +

  • This code is used in §4.
+

§4.1.1. Write the name field of the relation record4.1.1 =

@@ -279,8 +290,8 @@ combinations.
     Emit::array_text_entry(NF);
     DISCARD_TEXT(NF)
 
-
  • This code is used in §3.1.
-

§3.1.2. Write the permissions field of the relation record3.1.2 = +

  • This code is used in §4.1.
+

§4.1.2. Write the permissions field of the relation record4.1.2 =

@@ -296,7 +307,7 @@ combinations.
         Emit::array_iname_entry(Hierarchy::find(RELS_LOOKUP_ALL_X_HL));
         Emit::array_iname_entry(RELS_LIST_iname);
     }
-    switch(dbp->form_of_relation) {
+    switch(Relations::Explicit::get_form_of_relation(dbp)) {
         case Relation_Implicit:
             if ((minimal == FALSE) && (BinaryPredicates::can_be_made_true_at_runtime(dbp))) {
                 Emit::array_iname_entry(RELS_ASSERT_TRUE_iname);
@@ -304,26 +315,25 @@ combinations.
                 Emit::array_iname_entry(RELS_LOOKUP_ANY_iname);  Really?
             }
             break;
-        case Relation_OtoO: Emit::array_iname_entry(RELS_X_UNIQUE_iname); Emit::array_iname_entry(RELS_Y_UNIQUE_iname); Throw in the full suite3.1.2.1; break;
-        case Relation_OtoV: Emit::array_iname_entry(RELS_X_UNIQUE_iname); Throw in the full suite3.1.2.1; break;
-        case Relation_VtoO: Emit::array_iname_entry(RELS_Y_UNIQUE_iname); Throw in the full suite3.1.2.1; break;
+        case Relation_OtoO: Emit::array_iname_entry(RELS_X_UNIQUE_iname); Emit::array_iname_entry(RELS_Y_UNIQUE_iname); Throw in the full suite4.1.2.1; break;
+        case Relation_OtoV: Emit::array_iname_entry(RELS_X_UNIQUE_iname); Throw in the full suite4.1.2.1; break;
+        case Relation_VtoO: Emit::array_iname_entry(RELS_Y_UNIQUE_iname); Throw in the full suite4.1.2.1; break;
         case Relation_Sym_OtoO:
             Emit::array_iname_entry(RELS_SYMMETRIC_iname);
             Emit::array_iname_entry(RELS_X_UNIQUE_iname);
             Emit::array_iname_entry(RELS_Y_UNIQUE_iname);
-            Throw in the full suite3.1.2.1; break;
-        case Relation_Equiv: Emit::array_iname_entry(RELS_EQUIVALENCE_iname); Throw in the full suite3.1.2.1; break;
-        case Relation_VtoV: Throw in the full suite3.1.2.1; break;
-        case Relation_Sym_VtoV: Emit::array_iname_entry(RELS_SYMMETRIC_iname); Throw in the full suite3.1.2.1; break;
-        case Relation_ByRoutine: break;
+            Throw in the full suite4.1.2.1; break;
+        case Relation_Equiv: Emit::array_iname_entry(RELS_EQUIVALENCE_iname); Throw in the full suite4.1.2.1; break;
+        case Relation_VtoV: Throw in the full suite4.1.2.1; break;
+        case Relation_Sym_VtoV: Emit::array_iname_entry(RELS_SYMMETRIC_iname); Throw in the full suite4.1.2.1; break;
         default:
             internal_error("Binary predicate with unknown structural type");
     }
     Emit::array_end(save_sum);  of the summation, that is
     Emit::array_iname_entry(bm_symb);
 
-
  • This code is used in §3.1.
-

§3.1.2.1. Throw in the full suite3.1.2.1 = +

  • This code is used in §4.1.
+

§4.1.2.1. Throw in the full suite4.1.2.1 =

@@ -332,69 +342,72 @@ combinations.
     Emit::array_iname_entry(RELS_SHOW_iname);
     Emit::array_iname_entry(RELS_ROUTE_FIND_iname);
 
-
  • This code is used in §3.1.2 (7 times).
-

§3.1.3. The storage field has different meanings for different families of BPs: +

  • This code is used in §4.1.2 (7 times).
+

§4.1.3. The storage field has different meanings for different families of BPs:

-

Write the storage field of the relation metadata array3.1.3 = +

Write the storage field of the relation metadata array4.1.3 =

     binary_predicate *dbp = bp;
     if (bp->right_way_round == FALSE) dbp = bp->reversal;
-    switch(dbp->form_of_relation) {
-        case Relation_Implicit:  Field 0 is not used
-            Emit::array_numeric_entry(0);  which is not the same as NULL, unlike in C
-            break;
-        case Relation_OtoO:
-        case Relation_OtoV:
-        case Relation_VtoO:
-        case Relation_Sym_OtoO:
-        case Relation_Equiv:  Field 0 is the property used for run-time storage
-            Emit::array_iname_entry(Properties::iname(dbp->i6_storage_property));
-            break;
-        case Relation_VtoV:
-        case Relation_Sym_VtoV:  Field 0 is the bitmap array used for run-time storage
-            if (dbp->v2v_bitmap_iname == NULL) internal_error("gaah");
-            Emit::array_iname_entry(dbp->v2v_bitmap_iname);
-            break;
-        case Relation_ByRoutine: {  Field 0 is the routine used to test the relation
-            by_routine_bp_data *D = RETRIEVE_POINTER_by_routine_bp_data(dbp->family_specific);
-            Emit::array_iname_entry(D->bp_by_routine_iname);
-            break;
+    if (bp->relation_family == by_routine_bp_family) {
+         Field 0 is the routine used to test the relation
+        by_routine_bp_data *D = RETRIEVE_POINTER_by_routine_bp_data(dbp->family_specific);
+        Emit::array_iname_entry(D->bp_by_routine_iname);
+    } else {
+        switch(Relations::Explicit::get_form_of_relation(dbp)) {
+            case Relation_Implicit:  Field 0 is not used
+                Emit::array_numeric_entry(0);  which is not the same as NULL, unlike in C
+                break;
+            case Relation_OtoO:
+            case Relation_OtoV:
+            case Relation_VtoO:
+            case Relation_Sym_OtoO:
+            case Relation_Equiv:  Field 0 is the property used for run-time storage
+                Emit::array_iname_entry(
+                    Properties::iname(Relations::Explicit::get_i6_storage_property(dbp)));
+                break;
+            case Relation_VtoV:
+            case Relation_Sym_VtoV: {
+                 Field 0 is the bitmap array used for run-time storage
+                explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+                if (ED->v2v_bitmap_iname == NULL) internal_error("gaah");
+                Emit::array_iname_entry(ED->v2v_bitmap_iname);
+                break;
+            }
         }
-        default:
-            internal_error("Binary predicate with unknown structural type");
     }
 
-
  • This code is used in §3.1.
-

§3.1.4. Write the kind field of the relation record3.1.4 = +

  • This code is used in §4.1.
+

§4.1.4. Write the kind field of the relation record4.1.4 =

     Kinds::RunTime::emit_strong_id(BinaryPredicates::kind(bp));
 
-
  • This code is used in §3.1.
-

§3.1.5. Write the description field of the relation record3.1.5 = +

  • This code is used in §4.1.
+

§4.1.5. Write the description field of the relation record4.1.5 =

     TEMPORARY_TEXT(DF)
-    if (bp->form_of_relation == Relation_Implicit)
+    if (Relations::Explicit::get_form_of_relation(bp) == Relation_Implicit)
         WRITE_TO(DF, "%S", BinaryPredicates::get_log_name(bp));
     else CompiledText::from_text(DF, Node::get_text(bp->bp_created_at));
     Emit::array_text_entry(DF);
     DISCARD_TEXT(DF)
 
-
  • This code is used in §3.1.
-

§3.1.6. Write the handler field of the relation record3.1.6 = +

  • This code is used in §4.1.
+

§4.1.6. Write the handler field of the relation record4.1.6 =

     Emit::array_iname_entry(handler);
 
-
  • This code is used in §3.1.
-

§3.2. Write the relation handler routine for this BP3.2 = +

  • This code is used in §4.1.
+

§4.2. Write the relation handler routine for this BP4.2 =

@@ -402,7 +415,7 @@ combinations.
     binary_predicate *dbp = bp;
     if (bp->right_way_round == FALSE) { X = I"Y"; Y = I"X"; dbp = bp->reversal; }
 
-    handler = BinaryPredicates::handler_iname(bp);
+    handler = RTRelations::handler_iname(bp);
     packaging_state save = Routines::begin(handler);
     inter_symbol *rr_s = LocalVariables::add_named_call_as_symbol(I"rr");
     inter_symbol *task_s = LocalVariables::add_named_call_as_symbol(I"task");
@@ -427,7 +440,7 @@ combinations.
                 Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
                 Produce::code(Emit::tree());
                 Produce::down(Emit::tree());
-                    The TEST task3.2.4;
+                    The TEST task4.2.4;
                 Produce::up(Emit::tree());
             Produce::up(Emit::tree());
             if (minimal) {
@@ -435,7 +448,7 @@ combinations.
                 Produce::down(Emit::tree());
                     Produce::code(Emit::tree());
                     Produce::down(Emit::tree());
-                        The default case for minimal relations only3.2.1;
+                        The default case for minimal relations only4.2.1;
                     Produce::up(Emit::tree());
                 Produce::up(Emit::tree());
             } else {
@@ -444,7 +457,7 @@ combinations.
                     Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_LOOKUP_ANY_HL));
                     Produce::code(Emit::tree());
                     Produce::down(Emit::tree());
-                        The LOOKUP ANY task3.2.9;
+                        The LOOKUP ANY task4.2.9;
                     Produce::up(Emit::tree());
                 Produce::up(Emit::tree());
                 Produce::inv_primitive(Emit::tree(), CASE_BIP);
@@ -452,7 +465,7 @@ combinations.
                     Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_LOOKUP_ALL_X_HL));
                     Produce::code(Emit::tree());
                     Produce::down(Emit::tree());
-                        The LOOKUP ALL X task3.2.10;
+                        The LOOKUP ALL X task4.2.10;
                     Produce::up(Emit::tree());
                 Produce::up(Emit::tree());
                 Produce::inv_primitive(Emit::tree(), CASE_BIP);
@@ -460,7 +473,7 @@ combinations.
                     Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_LOOKUP_ALL_Y_HL));
                     Produce::code(Emit::tree());
                     Produce::down(Emit::tree());
-                        The LOOKUP ALL Y task3.2.11;
+                        The LOOKUP ALL Y task4.2.11;
                     Produce::up(Emit::tree());
                 Produce::up(Emit::tree());
                 Produce::inv_primitive(Emit::tree(), CASE_BIP);
@@ -468,7 +481,7 @@ combinations.
                     Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_LIST_HL));
                     Produce::code(Emit::tree());
                     Produce::down(Emit::tree());
-                        The LIST task3.2.12;
+                        The LIST task4.2.12;
                     Produce::up(Emit::tree());
                 Produce::up(Emit::tree());
                 if (BinaryPredicates::can_be_made_true_at_runtime(bp)) {
@@ -477,7 +490,7 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_ASSERT_TRUE_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The ASSERT TRUE task3.2.2;
+                            The ASSERT TRUE task4.2.2;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                     Produce::inv_primitive(Emit::tree(), CASE_BIP);
@@ -485,13 +498,13 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_ASSERT_FALSE_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The ASSERT FALSE task3.2.3;
+                            The ASSERT FALSE task4.2.3;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                 }
                 inter_name *shower = NULL;
                 int par = 0;
-                switch(dbp->form_of_relation) {
+                switch(Relations::Explicit::get_form_of_relation(dbp)) {
                     case Relation_OtoO: shower = Hierarchy::find(RELATION_RSHOWOTOO_HL); break;
                     case Relation_OtoV: shower = Hierarchy::find(RELATION_RSHOWOTOO_HL); break;
                     case Relation_VtoO: shower = Hierarchy::find(RELATION_SHOWOTOO_HL); break;
@@ -506,13 +519,13 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_SHOW_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The SHOW task3.2.7;
+                            The SHOW task4.2.7;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                 }
                 inter_name *emptier = NULL;
                 par = 0;
-                switch(dbp->form_of_relation) {
+                switch(Relations::Explicit::get_form_of_relation(dbp)) {
                     case Relation_OtoO: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
                     case Relation_OtoV: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
                     case Relation_VtoO: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
@@ -527,14 +540,14 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_EMPTY_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The EMPTY task3.2.8;
+                            The EMPTY task4.2.8;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                 }
                 inter_name *router = NULL;
                 int id_flag = TRUE;
                 int follow = FALSE;
-                switch(dbp->form_of_relation) {
+                switch(Relations::Explicit::get_form_of_relation(dbp)) {
                     case Relation_OtoO: router = Hierarchy::find(OTOVRELROUTETO_HL); follow = TRUE; break;
                     case Relation_OtoV: router = Hierarchy::find(OTOVRELROUTETO_HL); follow = TRUE; break;
                     case Relation_VtoO: router = Hierarchy::find(VTOORELROUTETO_HL); follow = TRUE; break;
@@ -550,7 +563,7 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_ROUTE_FIND_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The ROUTE FIND task3.2.5;
+                            The ROUTE FIND task4.2.5;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                     Produce::inv_primitive(Emit::tree(), CASE_BIP);
@@ -558,7 +571,7 @@ combinations.
                         Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_ROUTE_FIND_COUNT_HL));
                         Produce::code(Emit::tree());
                         Produce::down(Emit::tree());
-                            The ROUTE FIND COUNT task3.2.6;
+                            The ROUTE FIND COUNT task4.2.6;
                         Produce::up(Emit::tree());
                     Produce::up(Emit::tree());
                 }
@@ -569,8 +582,8 @@ combinations.
     Produce::rfalse(Emit::tree());
     Routines::end(save);
 
-
  • This code is used in §3.
-

§3.2.1. The default case for minimal relations only3.2.1 = +

  • This code is used in §4.
+

§4.2.1. The default case for minimal relations only4.2.1 =

@@ -582,8 +595,8 @@ combinations.
         Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::iname(bp));
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.2. The ASSERT TRUE task3.2.2 = +

  • This code is used in §4.2.
+

§4.2.2. The ASSERT TRUE task4.2.2 =

@@ -595,8 +608,8 @@ combinations.
         Produce::rtrue(Emit::tree());
     }
 
-
  • This code is used in §3.2.
-

§3.2.3. The ASSERT FALSE task3.2.3 = +

  • This code is used in §4.2.
+

§4.2.3. The ASSERT FALSE task4.2.3 =

@@ -608,8 +621,8 @@ combinations.
         Produce::rtrue(Emit::tree());
     }
 
-
  • This code is used in §3.2.
-

§3.2.4. The TEST task3.2.4 = +

  • This code is used in §4.2.
+

§4.2.4. The TEST task4.2.4 =

@@ -649,8 +662,8 @@ combinations.
     Produce::up(Emit::tree());
     Produce::rfalse(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.5. The ROUTE FIND task3.2.5 = +

  • This code is used in §4.2.
+

§4.2.5. The ROUTE FIND task4.2.5 =

@@ -659,14 +672,14 @@ combinations.
         Produce::inv_primitive(Emit::tree(), INDIRECT3_BIP);
         Produce::down(Emit::tree());
             Produce::val_iname(Emit::tree(), K_value, router);
-            Expand the ID operand3.2.5.1;
+            Expand the ID operand4.2.5.1;
             Produce::val_symbol(Emit::tree(), K_value, X_s);
             Produce::val_symbol(Emit::tree(), K_value, Y_s);
         Produce::up(Emit::tree());
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.5.1. Expand the ID operand3.2.5.1 = +

  • This code is used in §4.2.
+

§4.2.5.1. Expand the ID operand4.2.5.1 =

@@ -680,8 +693,8 @@ combinations.
         Produce::val_symbol(Emit::tree(), K_value, rr_s);
     }
 
- -

§3.2.6. The ROUTE FIND COUNT task3.2.6 = +

+

§4.2.6. The ROUTE FIND COUNT task4.2.6 =

@@ -693,7 +706,7 @@ combinations.
             Produce::inv_primitive(Emit::tree(), INDIRECT3_BIP);
             Produce::down(Emit::tree());
                 Produce::val_iname(Emit::tree(), K_value, router);
-                Expand the ID operand3.2.5.1;
+                Expand the ID operand4.2.5.1;
                 Produce::val_symbol(Emit::tree(), K_value, X_s);
                 Produce::val_symbol(Emit::tree(), K_value, Y_s);
             Produce::up(Emit::tree());
@@ -704,7 +717,7 @@ combinations.
         Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
         Produce::down(Emit::tree());
             Produce::val_iname(Emit::tree(), K_value, router);
-            Expand the ID operand3.2.5.1;
+            Expand the ID operand4.2.5.1;
             Produce::val_symbol(Emit::tree(), K_value, X_s);
             Produce::val_symbol(Emit::tree(), K_value, Y_s);
             Produce::val(Emit::tree(), K_truth_state, LITERAL_IVAL, 1);
@@ -712,8 +725,8 @@ combinations.
     }
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.7. The SHOW task3.2.7 = +

  • This code is used in §4.2.
+

§4.2.7. The SHOW task4.2.7 =

@@ -725,8 +738,8 @@ combinations.
     Produce::up(Emit::tree());
     Produce::rtrue(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.8. The EMPTY task3.2.8 = +

  • This code is used in §4.2.
+

§4.2.8. The EMPTY task4.2.8 =

@@ -745,8 +758,8 @@ combinations.
         Produce::up(Emit::tree());
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.9. The LOOKUP ANY task3.2.9 = +

  • This code is used in §4.2.
+

§4.2.9. The LOOKUP ANY task4.2.9 =

@@ -768,17 +781,17 @@ combinations.
         Produce::code(Emit::tree());
         Produce::down(Emit::tree());
             int t = 0;
-            Write rels lookup3.2.9.1;
+            Write rels lookup4.2.9.1;
         Produce::up(Emit::tree());
         Produce::code(Emit::tree());
         Produce::down(Emit::tree());
             t = 1;
-            Write rels lookup3.2.9.1;
+            Write rels lookup4.2.9.1;
         Produce::up(Emit::tree());
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.10. The LOOKUP ALL X task3.2.10 = +

  • This code is used in §4.2.
+

§4.2.10. The LOOKUP ALL X task4.2.10 =

@@ -789,15 +802,15 @@ combinations.
     Produce::up(Emit::tree());
 
     int t = 0;
-    Write rels lookup list3.2.10.1;
+    Write rels lookup list4.2.10.1;
 
     Produce::inv_primitive(Emit::tree(), RETURN_BIP);
     Produce::down(Emit::tree());
         Produce::val_symbol(Emit::tree(), K_value, Y_s);
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.11. The LOOKUP ALL Y task3.2.11 = +

  • This code is used in §4.2.
+

§4.2.11. The LOOKUP ALL Y task4.2.11 =

@@ -808,15 +821,15 @@ combinations.
     Produce::up(Emit::tree());
 
     int t = 1;
-    Write rels lookup list3.2.10.1;
+    Write rels lookup list4.2.10.1;
 
     Produce::inv_primitive(Emit::tree(), RETURN_BIP);
     Produce::down(Emit::tree());
         Produce::val_symbol(Emit::tree(), K_value, Y_s);
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.12. The LIST task3.2.12 = +

  • This code is used in §4.2.
+

§4.2.12. The LIST task4.2.12 =

@@ -836,7 +849,7 @@ combinations.
         Produce::code(Emit::tree());
         Produce::down(Emit::tree());
             int t = 0;
-            Write rels lookup list all3.2.12.1;
+            Write rels lookup list all4.2.12.1;
         Produce::up(Emit::tree());
         Produce::code(Emit::tree());
         Produce::down(Emit::tree());
@@ -850,7 +863,7 @@ combinations.
                 Produce::code(Emit::tree());
                 Produce::down(Emit::tree());
                     t = 1;
-                    Write rels lookup list all3.2.12.1;
+                    Write rels lookup list all4.2.12.1;
                 Produce::up(Emit::tree());
             Produce::up(Emit::tree());
         Produce::up(Emit::tree());
@@ -861,8 +874,8 @@ combinations.
         Produce::val_symbol(Emit::tree(), K_value, X_s);
     Produce::up(Emit::tree());
 
-
  • This code is used in §3.2.
-

§3.2.9.1. Write rels lookup3.2.9.1 = +

  • This code is used in §4.2.
+

§4.2.9.1. Write rels lookup4.2.9.1 =

@@ -878,7 +891,7 @@ combinations.
                     Produce::down(Emit::tree());
                         Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
                         Produce::down(Emit::tree());
-                            Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+                            Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
                             Produce::val_symbol(Emit::tree(), K_value, rr_s);
                             Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
                             if (t == 0) {
@@ -965,8 +978,8 @@ combinations.
         Produce::up(Emit::tree());
     }
 
-
  • This code is used in §3.2.9 (twice).
-

§3.2.10.1. Write rels lookup list3.2.10.1 = +

  • This code is used in §4.2.9 (twice).
+

§4.2.10.1. Write rels lookup list4.2.10.1 =

@@ -982,7 +995,7 @@ combinations.
                     Produce::down(Emit::tree());
                         Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
                         Produce::down(Emit::tree());
-                            Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+                            Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
                             Produce::val_symbol(Emit::tree(), K_value, rr_s);
                             Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
                             if (t == 0) {
@@ -1007,8 +1020,8 @@ combinations.
         }
     }
 
- -

§3.2.12.1. Write rels lookup list all3.2.12.1 = +

+

§4.2.12.1. Write rels lookup list all4.2.12.1 =

@@ -1029,7 +1042,7 @@ combinations.
                             Produce::down(Emit::tree());
                                 Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
                                 Produce::down(Emit::tree());
-                                    Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+                                    Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
                                     Produce::val_symbol(Emit::tree(), K_value, rr_s);
                                     Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
                                     Produce::val_symbol(Emit::tree(), K_value, Z1_s);
@@ -1062,13 +1075,13 @@ combinations.
         }
     }
 
- -

§4. And now a variation for default values: for example, an anonymous relation +

+

§5. And now a variation for default values: for example, an anonymous relation between numbers and texts.

-void RTRelations::compile_default_relation(inter_name *identifier, kind *K) {
+void RTRelations::compile_default_relation(inter_name *identifier, kind *K) {
     packaging_state save = Emit::named_array_begin(identifier, K_value);
     Kinds::RunTime::emit_block_value_header(K, FALSE, 8);
     Emit::array_null_entry();
@@ -1085,7 +1098,7 @@ between numbers and texts.
     Emit::array_end(save);
 }
 
-void RTRelations::compile_blank_relation(kind *K) {
+void RTRelations::compile_blank_relation(kind *K) {
     Kinds::RunTime::emit_block_value_header(K, FALSE, 34);
     Emit::array_null_entry();
     Emit::array_null_entry();
@@ -1110,7 +1123,7 @@ between numbers and texts.
     for (int i=0; i<24; i++) Emit::array_numeric_entry(0);
 }
 
-

§5. Support for the RELATIONS command.

+

§6. Support for the RELATIONS command.

 void RTRelations::IterateRelations(void) {
@@ -1130,7 +1143,7 @@ between numbers and texts.
     Hierarchy::make_available(Emit::tree(), iname);
 }
 
-

§6. The bitmap for various-to-various relations. It is unavoidable that a general V-to-V relation will take at least \(LR\) bits +

§7. The bitmap for various-to-various relations. It is unavoidable that a general V-to-V relation will take at least \(LR\) bits of storage, where \(L\) is the size of the left domain and \(R\) the size of the right domain. (A symmetric V-to-V relation needs only a little over \(LR/2\) bits, though in practice we don't want the nuisance of this memory saving.) Cheaper @@ -1140,7 +1153,7 @@ of that. Our strategy will therefore be to store these \(LR\) bits in the most direct way possible, with as little overhead as possible: in a bitmap.

-

§7. The following code compiles a stream of bits into a sequence of 16-bit +

§8. The following code compiles a stream of bits into a sequence of 16-bit I6 constants written in hexadecimal, padding out with 0s to fill any incomplete word left at the end. The first bit of the stream becomes the least significant bit of the first word of the output. @@ -1149,11 +1162,11 @@ bit of the first word of the output.

 int word_compiled = 0, bit_counter = 0, words_compiled;
 
-void RTRelations::begin_bit_stream(void) {
+void RTRelations::begin_bit_stream(void) {
     word_compiled = 0; bit_counter = 0; words_compiled = 0;
 }
 
-void RTRelations::compile_bit(int b) {
+void RTRelations::compile_bit(int b) {
     word_compiled += (b << bit_counter);
     bit_counter++;
     if (bit_counter == 16) {
@@ -1163,11 +1176,11 @@ bit of the first word of the output.
     }
 }
 
-void RTRelations::end_bit_stream(void) {
-    while (bit_counter != 0) RTRelations::compile_bit(0);
+void RTRelations::end_bit_stream(void) {
+    while (bit_counter != 0) RTRelations::compile_bit(0);
 }
 
-

§8. As was implied above, the run-time storage for a various to various relation +

§9. As was implied above, the run-time storage for a various to various relation whose BP has allocation ID number X is an I6 word array called V2V_Bitmap_X. This begins with a header of 8 words and is then followed by a bitmap.

@@ -1175,27 +1188,28 @@ This begins with a header of 8 words and is then followed by a bitmap.
 void RTRelations::compile_vtov_storage(binary_predicate *bp) {
     int left_count = 0, right_count = 0, words_used = 0, bytes_used = 0;
-    RTRelations::allocate_index_storage();
-    Index the left and right domains and calculate their sizes8.1;
+    RTRelations::allocate_index_storage();
+    Index the left and right domains and calculate their sizes9.1;
 
     inter_name *v2v_iname = NULL;
     if ((left_count > 0) && (right_count > 0))
-        Allocate a zeroed-out memory cache for relations with fast route-finding8.3;
+        Allocate a zeroed-out memory cache for relations with fast route-finding9.3;
 
     package_request *P = BinaryPredicates::package(bp);
-    bp->v2v_bitmap_iname = Hierarchy::make_iname_in(BITMAP_HL, P);
-    packaging_state save = Emit::named_array_begin(bp->v2v_bitmap_iname, K_value);
-    Compile header information in the V-to-V structure8.2;
+    explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+    ED->v2v_bitmap_iname = Hierarchy::make_iname_in(BITMAP_HL, P);
+    packaging_state save = Emit::named_array_begin(ED->v2v_bitmap_iname, K_value);
+    Compile header information in the V-to-V structure9.2;
 
     if ((left_count > 0) && (right_count > 0))
-        Compile bitmap pre-initialised to the V-to-V relation at start of play8.5;
+        Compile bitmap pre-initialised to the V-to-V relation at start of play9.5;
 
     Emit::array_end(save);
 
-    RTRelations::free_index_storage();
+    RTRelations::free_index_storage();
 }
 
-

§8.1. We calculate numbers \(L\) and \(R\), and index the items being related, so that +

§9.1. We calculate numbers \(L\) and \(R\), and index the items being related, so that the possible left values are indexed \(0, 1, 2, ..., L-1\) and the possible right values \(0, 1, 2, ..., R-1\). Note that in a relation such as

@@ -1217,18 +1231,18 @@ are already enumerated \(1, 2, 3, ..., N\) for some \(N\). The actual work in this is done by the routine RTRelations::relation_range (below).

-

Index the left and right domains and calculate their sizes8.1 = +

Index the left and right domains and calculate their sizes9.1 =

-    left_count = RTRelations::relation_range(bp, 0);
-    right_count = RTRelations::relation_range(bp, 1);
+    left_count = RTRelations::relation_range(bp, 0);
+    right_count = RTRelations::relation_range(bp, 1);
 
-
  • This code is used in §8.
-

§8.2. See "Relations.i6t" in the template layer for details. +

  • This code is used in §9.
+

§9.2. See "Relations.i6t" in the template layer for details.

-

Compile header information in the V-to-V structure8.2 = +

Compile header information in the V-to-V structure9.2 =

@@ -1254,8 +1268,8 @@ this is done by the routine R
         Emit::array_numeric_entry(0);
     words_used += 8;
 
-
  • This code is used in §8.
-

§8.3. Fast route finding is available only where the left and right domains are +

  • This code is used in §9.
+

§9.3. Fast route finding is available only where the left and right domains are equal, and even then, only when the user asked for it. If so, we allocate \(LR\) bytes as a cache if \(L=R<256\), and \(LR\) words otherwise. The cache is initialised to all-zeros, which saves an inordinate amount of nuisance, @@ -1263,7 +1277,7 @@ and this is why the "cache broken" flag is initially set in the header above: it forces the template layer to generate the cache when first used.

-

Allocate a zeroed-out memory cache for relations with fast route-finding8.3 = +

Allocate a zeroed-out memory cache for relations with fast route-finding9.3 =

@@ -1292,8 +1306,8 @@ above: it forces the template layer to generate the cache when first used.
         v2v_iname = Emit::named_numeric_constant(iname, 0);
     }
 
-
  • This code is used in §8.
-

§8.4. The following routine conveniently determines whether a given INFS is +

  • This code is used in §9.
+

§9.4. The following routine conveniently determines whether a given INFS is within the domain of one of the terms of a relation; the rule is that it mustn't itself express a domain (otherwise, e.g., the kind "woman" would show up as within the domain of "person" — we want only instances here, @@ -1301,7 +1315,7 @@ not kinds); and that it must inherit from the domain of the term.

-int RTRelations::infs_in_domain(inference_subject *infs, binary_predicate *bp, int index) {
+int RTRelations::infs_in_domain(inference_subject *infs, binary_predicate *bp, int index) {
     if (InferenceSubjects::domain(infs) != NULL) return FALSE;
     kind *K = BinaryPredicates::term_kind(bp, index);
     if (K == NULL) return FALSE;
@@ -1310,7 +1324,7 @@ not kinds); and that it must inherit from the domain of the term.
     return FALSE;
 }
 
-

§8.5. Now to assemble the bitmap. We do this by looking at inferences in the world-model +

§9.5. Now to assemble the bitmap. We do this by looking at inferences in the world-model to find out what pairs \((x, y)\) are such that assertions have declared that \(B(x, y)\) is true.

@@ -1324,30 +1338,30 @@ time, requiring us to store a whole row, but allowing the world-model code to send the pairs in that row in any order.

-

Compile bitmap pre-initialised to the V-to-V relation at start of play8.5 = +

Compile bitmap pre-initialised to the V-to-V relation at start of play9.5 =

     char *row_flags = Memory::malloc(right_count, RELATION_CONSTRUCTION_MREASON);
     if (row_flags) {
-        RTRelations::begin_bit_stream();
+        RTRelations::begin_bit_stream();
 
         inference_subject *infs;
         LOOP_OVER(infs, inference_subject)
-            if (RTRelations::infs_in_domain(infs, bp, 0)) {
+            if (RTRelations::infs_in_domain(infs, bp, 0)) {
                 int j;
                 for (j=0; j<right_count; j++) row_flags[j] = 0;
-                Find all pairs belonging to this row, and set the relevant flags8.5.1;
-                for (j=0; j<right_count; j++) RTRelations::compile_bit(row_flags[j]);
+                Find all pairs belonging to this row, and set the relevant flags9.5.1;
+                for (j=0; j<right_count; j++) RTRelations::compile_bit(row_flags[j]);
             }
 
-        RTRelations::end_bit_stream();
+        RTRelations::end_bit_stream();
         words_used += words_compiled;
         Memory::I7_free(row_flags, RELATION_CONSTRUCTION_MREASON, right_count);
     }
 
-
  • This code is used in §8.
-

§8.5.1. Find all pairs belonging to this row, and set the relevant flags8.5.1 = +

  • This code is used in §9.
+

§9.5.1. Find all pairs belonging to this row, and set the relevant flags9.5.1 =

@@ -1355,53 +1369,53 @@ to send the pairs in that row in any order.
     POSITIVE_KNOWLEDGE_LOOP(inf, BinaryPredicates::as_subject(bp), ARBITRARY_RELATION_INF) {
         inference_subject *left_infs, *right_infs;
         World::Inferences::get_references(inf, &left_infs, &right_infs);
-        if (infs == left_infs) row_flags[RTRelations::get_relation_index(right_infs, 1)] = 1;
+        if (infs == left_infs) row_flags[RTRelations::get_relation_index(right_infs, 1)] = 1;
     }
 
-
  • This code is used in §8.5.
-

§9. Lastly on this: the way we count and index the left (index=0) or right (1) +

  • This code is used in §9.5.
+

§10. Lastly on this: the way we count and index the left (index=0) or right (1) domain. We count upwards from 0 (in order of creation).

-int RTRelations::relation_range(binary_predicate *bp, int index) {
+int RTRelations::relation_range(binary_predicate *bp, int index) {
     int t = 0;
     inference_subject *infs;
     LOOP_OVER(infs, inference_subject) {
-        if (RTRelations::infs_in_domain(infs, bp, index)) RTRelations::set_relation_index(infs, index, t++);
-        else RTRelations::set_relation_index(infs, index, -1);
+        if (RTRelations::infs_in_domain(infs, bp, index)) RTRelations::set_relation_index(infs, index, t++);
+        else RTRelations::set_relation_index(infs, index, -1);
     }
     return t;
 }
 
-

§10. Tiresomely, we have to store these indices for a little while, so: +

§11. Tiresomely, we have to store these indices for a little while, so:

 int *relation_indices = NULL;
-void RTRelations::allocate_index_storage(void) {
+void RTRelations::allocate_index_storage(void) {
     int nc = NUMBER_CREATED(inference_subject);
     relation_indices = (int *) (Memory::calloc(nc, 2*sizeof(int), OBJECT_COMPILATION_MREASON));
 }
 
-void RTRelations::set_relation_index(inference_subject *infs, int i, int v) {
+void RTRelations::set_relation_index(inference_subject *infs, int i, int v) {
     if (relation_indices == NULL) internal_error("relation index unallocated");
     relation_indices[2*(infs->allocation_id) + i] = v;
 }
 
-int RTRelations::get_relation_index(inference_subject *infs, int i) {
+int RTRelations::get_relation_index(inference_subject *infs, int i) {
     if (relation_indices == NULL) internal_error("relation index unallocated");
     return relation_indices[2*(infs->allocation_id) + i];
 }
 
-void RTRelations::free_index_storage(void) {
+void RTRelations::free_index_storage(void) {
     if (relation_indices == NULL) internal_error("relation index unallocated");
     int nc = NUMBER_CREATED(inference_subject);
     Memory::I7_array_free(relation_indices, OBJECT_COMPILATION_MREASON, nc, 2*sizeof(int));
     relation_indices = NULL;
 }
 
-

§11. The partition for an equivalence relation. An equivalence relation \(E\) is such that \(E(x, x)\) for all \(x\), such that +

§12. The partition for an equivalence relation. An equivalence relation \(E\) is such that \(E(x, x)\) for all \(x\), such that \(E(x, y)\) if and only if \(E(y, x)\), and such that \(E(x, y)\) and \(E(y, z)\) together imply \(E(x, z)\): the properties of being reflexive, symmetric and transitive. The relation constructed by a sentence like @@ -1460,7 +1474,7 @@ alliance then maintain the collective values of the -

§12. We calculate the initial partition by starting with the sparsest possible +

§13. We calculate the initial partition by starting with the sparsest possible equivalence relation, \(E(x, y)\) if and only if \(x=y\), where each member is related only to itself. (This is the equality relation.) The partition function here is given by \(p(x)\) equals the allocation ID number for object @@ -1479,15 +1493,15 @@ $$ p(P) = 12, p(S) = 23, p(R) = 25, p(D) = 26, p(O) = 31. $$

 void RTRelations::equivalence_relation_make_singleton_partitions(binary_predicate *bp,
     int domain_size) {
-    if (bp->form_of_relation != Relation_Equiv)
+    if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
         internal_error("attempt to make partition for a non-equivalence relation");
-    equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
+    explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
     int *partition_array = Memory::calloc(domain_size, sizeof(int), PARTITION_MREASON);
     for (int i=0; i<domain_size; i++) partition_array[i] = i+1;
-    D->equivalence_partition = partition_array;
+    D->equiv_data->equivalence_partition = partition_array;
 }
 
-

§13. The A-parser has meanwhile been reading in facts about the helping relation: +

§14. The A-parser has meanwhile been reading in facts about the helping relation:

@@ -1528,11 +1542,11 @@ users to set up these relations in a stylistically poor way.
 void RTRelations::equivalence_relation_merge_classes(binary_predicate *bp,
     int domain_size, int ix1, int ix2) {
-    if (bp->form_of_relation != Relation_Equiv)
+    if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
         internal_error("attempt to merge classes for a non-equivalence relation");
-    equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
+    explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
     if (bp->right_way_round == FALSE) bp = bp->reversal;
-    int *partition_array = D->equivalence_partition;;
+    int *partition_array = D->equiv_data->equivalence_partition;;
     if (partition_array == NULL)
         internal_error("attempt to use null equivalence partition array");
     int little, big;  or, The Fairies' Parliament
@@ -1545,7 +1559,7 @@ users to set up these relations in a stylistically poor way.
             partition_array[i] = little;
 }
 
-

§14. Once that process has completed, the code which compiles the +

§15. Once that process has completed, the code which compiles the initial state of the I6 object tree calls the following routine to ask it to fill in the (let's say) p63_helping property for each person in turn. @@ -1558,42 +1572,43 @@ in turn. instance *I; LOOP_OVER_INSTANCES(I, k) { inference_subject *infs = Instances::as_subject(I); - Set the partition number property14.1; + Set the partition number property15.1; } } else { instance *nc; LOOP_OVER_INSTANCES(nc, k) { inference_subject *infs = Instances::as_subject(nc); - Set the partition number property14.1; + Set the partition number property15.1; } } }

-

§14.1. Set the partition number property14.1 = +

§15.1. Set the partition number property15.1 =

     parse_node *val = Rvalues::from_int(
-        RTRelations::equivalence_relation_get_class(bp, infs->allocation_id), EMPTY_WORDING);
-    Properties::Valued::assert(bp->i6_storage_property, infs, val, CERTAIN_CE);
+        RTRelations::equivalence_relation_get_class(bp, infs->allocation_id), EMPTY_WORDING);
+    Properties::Valued::assert(Relations::Explicit::get_i6_storage_property(bp),
+        infs, val, CERTAIN_CE);
 
-
  • This code is used in §14 (twice).
-

§15. Where: +

  • This code is used in §15 (twice).
+

§16. Where:

-int RTRelations::equivalence_relation_get_class(binary_predicate *bp, int ix) {
-    if (bp->form_of_relation != Relation_Equiv)
+int RTRelations::equivalence_relation_get_class(binary_predicate *bp, int ix) {
+    if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
         internal_error("attempt to merge classes for a non-equivalence relation");
     if (bp->right_way_round == FALSE) bp = bp->reversal;
-    equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
-    int *partition_array = D->equivalence_partition;;
+    explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+    int *partition_array = D->equiv_data->equivalence_partition;;
     if (partition_array == NULL)
         internal_error("attempt to use null equivalence partition array");
     return partition_array[ix];
 }
 
-

§16. The following provides for run-time checking to make sure relations are +

§17. The following provides for run-time checking to make sure relations are not used with the wrong kinds of object. (Compile-time checking excludes other cases.)

@@ -1617,7 +1632,7 @@ other cases.) } relation_guard;
  • The structure relation_guard is private to this section.
-

§17. Generating routines to test relations by condition. When a relation has to be tested as a condition, we can't simply embed that +

§18. Generating routines to test relations by condition. When a relation has to be tested as a condition, we can't simply embed that condition as the I6 schema for "test relation": it might very well need local variables, the table row-choosing variables, etc., to evaluate. It has to be tested in its own context. So we generate a routine called @@ -1631,32 +1646,32 @@ whether or not $R(t_0<

 void RTRelations::compile_defined_relations(void) {
-    RTRelations::compile_relation_records();
+    RTRelations::compile_relation_records();
     binary_predicate *bp;
     LOOP_OVER(bp, binary_predicate)
-        if ((bp->form_of_relation == Relation_ByRoutine) && (bp->right_way_round)) {
+        if ((bp->relation_family == by_routine_bp_family) && (bp->right_way_round)) {
             current_sentence = bp->bp_created_at;
             TEMPORARY_TEXT(C)
             WRITE_TO(C, "Routine to decide if %S(t_0, t_1)", BinaryPredicates::get_log_name(bp));
             Produce::comment(Emit::tree(), C);
             DISCARD_TEXT(C)
             by_routine_bp_data *D = RETRIEVE_POINTER_by_routine_bp_data(bp->family_specific);
-            RTRelations::compile_routine_to_decide(D->bp_by_routine_iname,
+            RTRelations::compile_routine_to_decide(D->bp_by_routine_iname,
                 D->condition_defn_text, bp->term_details[0], bp->term_details[1]);
         }
-    Compile RProperty routine17.1;
+    Compile RProperty routine18.1;
 
     relation_guard *rg;
     LOOP_OVER(rg, relation_guard) {
-        Compile RGuard f0 routine17.2;
-        Compile RGuard f1 routine17.3;
-        Compile RGuard T routine17.4;
-        Compile RGuard MT routine17.5;
-        Compile RGuard MF routine17.6;
+        Compile RGuard f0 routine18.2;
+        Compile RGuard f1 routine18.3;
+        Compile RGuard T routine18.4;
+        Compile RGuard MT routine18.5;
+        Compile RGuard MF routine18.6;
     }
 }
 
-

§17.1. Compile RProperty routine17.1 = +

§18.1. Compile RProperty routine18.1 =

@@ -1690,8 +1705,8 @@ whether or not $R(t_0<
     Produce::up(Emit::tree());
     Routines::end(save);
 
-
  • This code is used in §17.
-

§17.2. Compile RGuard f0 routine17.2 = +

  • This code is used in §18.
+

§18.2. Compile RGuard f0 routine18.2 =

@@ -1732,8 +1747,8 @@ whether or not $R(t_0<
         Routines::end(save);
     }
 
-
  • This code is used in §17.
-

§17.3. Compile RGuard f1 routine17.3 = +

  • This code is used in §18.
+

§18.3. Compile RGuard f1 routine18.3 =

@@ -1774,8 +1789,8 @@ whether or not $R(t_0<
         Routines::end(save);
     }
 
-
  • This code is used in §17.
-

§17.4. Compile RGuard T routine17.4 = +

  • This code is used in §18.
+

§18.4. Compile RGuard T routine18.4 =

@@ -1823,8 +1838,8 @@ whether or not $R(t_0<
         Routines::end(save);
     }
 
-
  • This code is used in §17.
-

§17.5. Compile RGuard MT routine17.5 = +

  • This code is used in §18.
+

§18.5. Compile RGuard MT routine18.5 =

@@ -1882,8 +1897,8 @@ whether or not $R(t_0<
         Routines::end(save);
     }
 
-
  • This code is used in §17.
-

§17.6. Compile RGuard MF routine17.6 = +

  • This code is used in §18.
+

§18.6. Compile RGuard MF routine18.6 =

@@ -1941,18 +1956,18 @@ whether or not $R(t_0<
         Routines::end(save);
     }
 
-
  • This code is used in §17.
-

§18.

+
  • This code is used in §18.
+

§19.

-void RTRelations::compile_routine_to_decide(inter_name *rname,
+void RTRelations::compile_routine_to_decide(inter_name *rname,
     wording W, bp_term_details par1, bp_term_details par2) {
 
     packaging_state save = Routines::begin(rname);
 
     ph_stack_frame *phsf = Frames::current_stack_frame();
-    RTRelations::add_term_as_call_parameter(phsf, par1);
-    RTRelations::add_term_as_call_parameter(phsf, par2);
+    RTRelations::add_term_as_call_parameter(phsf, par1);
+    RTRelations::add_term_as_call_parameter(phsf, par2);
 
     LocalVariables::enable_possessive_form_of_it();
 
@@ -1973,14 +1988,14 @@ whether or not $R(t_0<
     Routines::end(save);
 }
 
-

§19. The following routine adds the given BP term as a call parameter to the +

§20. The following routine adds the given BP term as a call parameter to the routine currently being compiled, deciding that something is an object if its kind indications are all blank, but verifying that the value supplied matches the specific necessary kind of object if there is one.

-void RTRelations::add_term_as_call_parameter(ph_stack_frame *phsf,
+void RTRelations::add_term_as_call_parameter(ph_stack_frame *phsf,
     bp_term_details bptd) {
     kind *K = BPTerms::kind(&bptd);
     kind *PK = K;
@@ -2006,7 +2021,7 @@ matches the specific necessary kind of object if there is one.
     }
 }
 
-

§20. Indexing relations. A brief table of relations appears on the Phrasebook Index page. +

§21. Indexing relations. A brief table of relations appears on the Phrasebook Index page.

@@ -2039,11 +2054,11 @@ matches the specific necessary kind of object if there is one.
     HTML_CLOSE("p");
 }
 
-

§21. And a briefer note still for the table of verbs. +

§22. And a briefer note still for the table of verbs.

-void RTRelations::index_for_verbs(OUTPUT_STREAM, binary_predicate *bp) {
+void RTRelations::index_for_verbs(OUTPUT_STREAM, binary_predicate *bp) {
     WRITE(" ... <i>");
     if (bp == NULL) WRITE("(a meaning internal to Inform)");
     else {
diff --git a/docs/runtime-module/4-rsfk.html b/docs/runtime-module/4-rsfk.html
index 838f56a8c..34670b4ba 100644
--- a/docs/runtime-module/4-rsfk.html
+++ b/docs/runtime-module/4-rsfk.html
@@ -125,7 +125,7 @@ kinds being compiled to an I6 
 
 
-inter_name *Kinds::RunTime::I6_classname(kind *K) {
+inter_name *Kinds::RunTime::I6_classname(kind *K) {
     if (Kinds::Behaviour::is_object(K)) return Kinds::RunTime::iname(K);
     internal_error("no I6 classname available");
     return NULL;
@@ -222,7 +222,7 @@ chosen), but no problem message has been issued about this, or
             package_request *PR = Hierarchy::package_in_enclosure(BLOCK_CONSTANTS_HAP);
             inter_name *N = Hierarchy::make_iname_in(BLOCK_CONSTANT_HL, PR);
             packaging_state save = Emit::named_late_array_begin(N, K_value);
-            RTRelations::compile_blank_relation(K);
+            RTRelations::compile_blank_relation(K);
             Emit::array_end(save);
             if (N) Emit::holster(VH, N);
         } else {
@@ -673,7 +673,7 @@ turns up. This means remembering everything we've seen, using a new structure:
 

-void Kinds::RunTime::emit_strong_id(kind *K) {
+void Kinds::RunTime::emit_strong_id(kind *K) {
     runtime_kind_structure *rks = Kinds::RunTime::get_rks(K);
     if (rks) {
         Emit::array_iname_entry(rks->rks_iname);
@@ -682,7 +682,7 @@ turns up. This means remembering everything we've seen, using a new structure:
     }
 }
 
-void Kinds::RunTime::emit_strong_id_as_val(kind *K) {
+void Kinds::RunTime::emit_strong_id_as_val(kind *K) {
     runtime_kind_structure *rks = Kinds::RunTime::get_rks(K);
     if (rks) {
         Produce::val_iname(Emit::tree(), K_value, rks->rks_iname);
@@ -918,7 +918,7 @@ recursively scanned through for us, so that if we have seen a construction
     if (Kinds::get_construct(K) == CON_phrase) {
         Phrases::Constants::compile_default_closure(identifier, K);
     } else if (Kinds::get_construct(K) == CON_relation) {
-        RTRelations::compile_default_relation(identifier, K);
+        RTRelations::compile_default_relation(identifier, K);
     } else if (Kinds::get_construct(K) == CON_list_of) {
         Lists::compile_default_list(identifier, K);
     } else {
@@ -1082,7 +1082,7 @@ list of 20 texts. For the cases above, it's always 1.
 define BLK_FLAG_TRUNCMULT 0x00000010
 
-void Kinds::RunTime::emit_block_value_header(kind *K, int individual, int size) {
+void Kinds::RunTime::emit_block_value_header(kind *K, int individual, int size) {
     if (individual == FALSE) Emit::array_numeric_entry(0);
     int n = 0, c = 1, w = 4;
     if (TargetVMs::is_16_bit(Task::vm())) w = 2;
diff --git a/docs/runtime-module/4-rtn.html b/docs/runtime-module/4-rtn.html
index e89b54ad4..cfdcb3399 100644
--- a/docs/runtime-module/4-rtn.html
+++ b/docs/runtime-module/4-rtn.html
@@ -81,7 +81,7 @@ already been set up, or not. Here's not:
 

-packaging_state Routines::begin(inter_name *name) {
+packaging_state Routines::begin(inter_name *name) {
     return Routines::begin_framed(name, NULL);
 }
 
@@ -140,7 +140,7 @@ did not.

-void Routines::end(packaging_state save) {
+void Routines::end(packaging_state save) {
     kind *R_kind = LocalVariables::deduced_function_kind(currently_compiling_in_frame);
 
     inter_name *kernel_name = NULL, *public_name = currently_compiling_iname;
diff --git a/docs/runtime-module/4-vart.html b/docs/runtime-module/4-vart.html
index 5feaf851f..2b2c97e06 100644
--- a/docs/runtime-module/4-vart.html
+++ b/docs/runtime-module/4-vart.html
@@ -635,7 +635,7 @@ usages to the debugging log.
             if (vu->where_vu_created)
                 Index::link(OUT, Wordings::first_wn(Node::get_text(vu->where_vu_created)));
             binary_predicate *bp = VerbMeanings::get_regular_meaning_of_form(Verbs::base_form(VerbUsages::get_verb(vu)));
-            if (bp) RTRelations::index_for_verbs(OUT, bp);
+            if (bp) RTRelations::index_for_verbs(OUT, bp);
             return;
         }
     preposition *prep;
@@ -644,7 +644,7 @@ usages to the debugging log.
             if (prep->where_prep_created)
                 Index::link(OUT, Wordings::first_wn(Node::get_text(prep->where_prep_created)));
             binary_predicate *bp = VerbMeanings::get_regular_meaning_of_form(Verbs::find_form(copular_verb, prep, NULL));
-            if (bp) RTRelations::index_for_verbs(OUT, bp);
+            if (bp) RTRelations::index_for_verbs(OUT, bp);
             return;
         }
 }
diff --git a/inform7/Figures/memory-diagnostics.txt b/inform7/Figures/memory-diagnostics.txt
index c75cfad81..4a2f4c0c2 100644
--- a/inform7/Figures/memory-diagnostics.txt
+++ b/inform7/Figures/memory-diagnostics.txt
@@ -1,6 +1,6 @@
-Total memory consumption was 256345K = 250 MB
+Total memory consumption was 256344K = 250 MB
 
-62.4% was used for 1338375 objects, in 273578 frames in 200 x 800K = 160000K = 156 MB:
+62.4% was used for 1338387 objects, in 273590 frames in 200 x 800K = 160000K = 156 MB:
 
      9.8%  inter_tree_node_array                    36 x 8192 = 294912 objects, 25953408 bytes
      5.5%  text_stream_array                        2567 x 100 = 256700 objects, 14457344 bytes
@@ -29,8 +29,8 @@ Total memory consumption was 256345K = 250 MB
      0.2%  dictionary                               16348 objects, 784704 bytes
      0.2%  dict_entry_array                         233 x 100 = 23300 objects, 753056 bytes
      0.2%  package_request                          7931 objects, 697928 bytes
-     0.2%  inter_name_generator_array               16 x 1000 = 16000 objects, 640512 bytes
      0.2%  unary_predicate_array                    16 x 1000 = 16000 objects, 640512 bytes
+     0.2%  inter_name_generator_array               16 x 1000 = 16000 objects, 640512 bytes
      0.1%  local_variable_array                     45 x 100 = 4500 objects, 505440 bytes
      0.1%  inference_subject                        664 objects, 435584 bytes
      0.1%  verb_usage                               1128 objects, 388032 bytes
@@ -41,8 +41,8 @@ Total memory consumption was 256345K = 250 MB
      0.1%  noun                                     2379 objects, 285480 bytes
      ----  action_name_list_array                   3 x 1000 = 3000 objects, 240096 bytes
      ----  inter_annotation_array                   1 x 8192 objects, 196640 bytes
-     ----  binary_predicate                         321 objects, 184896 bytes
      ----  inference                                1703 objects, 177112 bytes
+     ----  binary_predicate                         321 objects, 174624 bytes
      ----  linked_list_item_array                   10 x 1000 = 10000 objects, 160320 bytes
      ----  linguistic_stock_item                    3315 objects, 159120 bytes
      ----  stacked_variable_owner_list_array        38 x 100 = 3800 objects, 153216 bytes
@@ -91,8 +91,8 @@ Total memory consumption was 256345K = 250 MB
      ----  match_avinue_array                       1 x 1000 objects, 16032 bytes
      ----  to_phrase_request                        59 objects, 15576 bytes
      ----  adjective_iname_holder                   320 objects, 15360 bytes
+     ----  method                                   280 objects, 13440 bytes
      ----  plugin                                   23 objects, 13432 bytes
-     ----  method                                   275 objects, 13200 bytes
      ----  literal_text                             147 objects, 12936 bytes
      ----  stopwatch_timer                          148 objects, 11840 bytes
      ----  understanding_reference_array            2 x 100 = 200 objects, 11264 bytes
@@ -121,15 +121,15 @@ Total memory consumption was 256345K = 250 MB
      ----  property_setting_bp_data                 84 objects, 3360 bytes
      ----  kind_constructor_comparison_schema_array 1 x 100 objects, 3232 bytes
      ----  instance_usage_array                     1 x 200 objects, 3232 bytes
-     ----  compatibility_specification              66 objects, 3168 bytes
      ----  definition                               44 objects, 3168 bytes
+     ----  compatibility_specification              66 objects, 3168 bytes
      ----  inform_extension                         19 objects, 3040 bytes
      ----  property_of_value_storage                93 objects, 2976 bytes
      ----  submodule_request                        72 objects, 2880 bytes
      ----  inter_construct                          32 objects, 2560 bytes
+     ----  method_set                               77 objects, 2464 bytes
      ----  kind_constructor_casting_rule_array      1 x 100 objects, 2432 bytes
      ----  kind_constructor_instance_array          1 x 100 objects, 2432 bytes
-     ----  method_set                               76 objects, 2432 bytes
      ----  equation_symbol                          30 objects, 2400 bytes
      ----  semver_range                             22 objects, 2288 bytes
      ----  use_option                               29 objects, 1856 bytes
@@ -164,60 +164,61 @@ Total memory consumption was 256345K = 250 MB
      ----  rulebook_outcome                         17 objects, 680 bytes
      ----  inform_language                          6 objects, 672 bytes
      ----  inter_warehouse_room                     10 objects, 640 bytes
-     ----  relation_guard                           5 objects, 640 bytes
      ----  I6T_intervention                         8 objects, 640 bytes
+     ----  relation_guard                           5 objects, 640 bytes
      ----  nascent_array                            7 objects, 616 bytes
      ----  inbuild_search_result                    15 objects, 600 bytes
      ----  named_rulebook_outcome                   15 objects, 600 bytes
      ----  label_namespace                          10 objects, 560 bytes
      ----  small_word_set                           11 objects, 528 bytes
      ----  inform_kit                               5 objects, 520 bytes
-     ----  i6_memory_setting                        13 objects, 416 bytes
      ----  equation                                 4 objects, 416 bytes
+     ----  i6_memory_setting                        13 objects, 416 bytes
      ----  module_package                           10 objects, 400 bytes
      ----  dval_written                             10 objects, 400 bytes
+     ----  bp_family                                12 objects, 384 bytes
      ----  article_usage                            8 objects, 384 bytes
      ----  source_file                              5 objects, 360 bytes
-     ----  bp_family                                11 objects, 352 bytes
      ----  inbuild_genre                            7 objects, 336 bytes
-     ----  door_dir_notice                          5 objects, 320 bytes
-     ----  grammatical_category                     8 objects, 320 bytes
      ----  pronoun                                  8 objects, 320 bytes
+     ----  grammatical_category                     8 objects, 320 bytes
+     ----  door_dir_notice                          5 objects, 320 bytes
      ----  up_family                                9 objects, 288 bytes
      ----  build_step                               4 objects, 288 bytes
      ----  door_to_notice                           5 objects, 280 bytes
+     ----  explicit_bp_data                         5 objects, 280 bytes
      ----  inform_pipeline                          4 objects, 256 bytes
      ----  verb_usage_tier                          5 objects, 240 bytes
      ----  test_scenario                            1 object, 208 bytes
      ----  build_skill                              5 objects, 200 bytes
      ----  compilation_unit                         5 objects, 200 bytes
-     ----  plural_dictionary_entry                  4 objects, 192 bytes
      ----  kit_dependency                           4 objects, 192 bytes
+     ----  plural_dictionary_entry                  4 objects, 192 bytes
      ----  inform_project                           1 object, 176 bytes
-     ----  code_generation_target                   4 objects, 160 bytes
-     ----  link_instruction                         4 objects, 160 bytes
-     ----  pointer_allocation                       2 objects, 160 bytes
      ----  inter_architecture                       4 objects, 160 bytes
+     ----  code_generation_target                   4 objects, 160 bytes
+     ----  pointer_allocation                       2 objects, 160 bytes
+     ----  link_instruction                         4 objects, 160 bytes
      ----  codegen_pipeline                         1 object, 128 bytes
      ----  element_activation                       4 objects, 128 bytes
      ----  inbuild_nest                             3 objects, 120 bytes
      ----  inform_kit_ittt                          2 objects, 96 bytes
-     ----  article                                  2 objects, 80 bytes
-     ----  compile_task_data                        1 object, 80 bytes
      ----  list_together_routine                    2 objects, 80 bytes
-     ----  inter_warehouse                          1 object, 56 bytes
+     ----  compile_task_data                        1 object, 80 bytes
+     ----  article                                  2 objects, 80 bytes
      ----  build_methodology                        1 object, 56 bytes
+     ----  inter_warehouse                          1 object, 56 bytes
      ----  HTML_file_state                          1 object, 48 bytes
      ----  star_invention                           1 object, 48 bytes
      ----  blorb_figure                             1 object, 48 bytes
      ----  parse_name_notice                        1 object, 40 bytes
-     ----  kind_template_definition                 1 object, 40 bytes
      ----  by_routine_bp_data                       1 object, 40 bytes
+     ----  kind_template_definition                 1 object, 40 bytes
      ----  loop_over_scope                          1 object, 40 bytes
 
 37.5% was used for memory not allocated for objects:
 
-    15.9%  text stream storage                      41841836 bytes in 263036 claims
+    15.9%  text stream storage                      41840492 bytes in 263034 claims
      3.5%  dictionary storage                       9266176 bytes in 16348 claims
      ----  sorting                                  1048 bytes in 3 claims
      2.7%  source text                              7200000 bytes in 3 claims
@@ -234,5 +235,5 @@ Total memory consumption was 256345K = 250 MB
      ----  emitter array storage                    14368 bytes in 8 claims
      ----  code generation workspace for objects    9200 bytes in 9 claims
 
-20.0% was overhead - 52570000 bytes = 51337K = 50 MB
+20.0% was overhead - 52579688 bytes = 51347K = 50 MB
 
diff --git a/inform7/Figures/timings-diagnostics.txt b/inform7/Figures/timings-diagnostics.txt
index b472b6c98..8537a03cb 100644
--- a/inform7/Figures/timings-diagnostics.txt
+++ b/inform7/Figures/timings-diagnostics.txt
@@ -1,35 +1,36 @@
 100.0% in inform7 run
-     66.6% in compilation to Inter
+     66.2% in compilation to Inter
          25.3% in //Phrases::Manager::compile_first_block//
-          8.7% in //Phrases::Manager::compile_as_needed//
-          6.9% in //Strings::compile_responses//
-          6.1% in //World::Compile::compile//
-          3.7% in //MajorNodes::pre_pass//
-          3.3% in //MajorNodes::pass_1//
+          9.0% in //Phrases::Manager::compile_as_needed//
+          7.0% in //Strings::compile_responses//
+          5.9% in //World::Compile::compile//
+          3.5% in //MajorNodes::pre_pass//
+          3.2% in //MajorNodes::pass_1//
           2.0% in //Phrases::Manager::RulePrintingRule_routine//
           1.8% in //Phrases::Manager::rulebooks_array//
-          1.1% in //RTVerbs::ConjugateVerb//
-          0.7% in //Phrases::Manager::traverse//
+          1.0% in //RTVerbs::ConjugateVerb//
+          0.8% in //Phrases::Manager::traverse//
+          0.5% in //Phrases::Manager::compile_rulebooks//
           0.5% in //Phrases::Manager::parse_rule_parameters//
           0.5% in //World::complete//
           0.3% in //MajorNodes::pass_2//
-          0.3% in //Phrases::Manager::compile_rulebooks//
           0.3% in //RTRelations::compile_defined_relations//
           0.1% in //Kinds::RunTime::compile_data_type_support_routines//
           0.1% in //PL::Parsing::Verbs::compile_all//
           0.1% in //Task::make_built_in_kind_constructors//
-          3.8% not specifically accounted for
-     30.9% in running Inter pipeline
-         10.0% in step preparation
-          9.7% in inter step 2/12: link
-          7.2% in inter step 12/12: generate inform6 -> auto.inf
+          3.4% not specifically accounted for
+     31.5% in running Inter pipeline
+         10.9% in step preparation
+         10.1% in inter step 2/12: link
+          7.3% in inter step 12/12: generate inform6 -> auto.inf
           0.3% in inter step 9/12: make-identifiers-unique
           0.1% in inter step 10/12: reconcile-verbs
           0.1% in inter step 11/12: eliminate-redundant-labels
+          0.1% in inter step 4/12: parse-linked-matter
           0.1% in inter step 5/12: resolve-conditional-compilation
           0.1% in inter step 6/12: assimilate
           0.1% in inter step 7/12: resolve-external-symbols
           0.1% in inter step 8/12: inspect-plugs
-          2.4% not specifically accounted for
-      2.0% in supervisor
+          1.6% not specifically accounted for
+      1.8% in supervisor
       0.4% not specifically accounted for
diff --git a/inform7/assertions-module/Chapter 3/New Relation Requests.w b/inform7/assertions-module/Chapter 3/New Relation Requests.w
index 0f13b2921..78097df74 100644
--- a/inform7/assertions-module/Chapter 3/New Relation Requests.w	
+++ b/inform7/assertions-module/Chapter 3/New Relation Requests.w	
@@ -39,8 +39,8 @@ int RelationRequests::new_relation_SMF(int task, parse_node *V, wording *NPs) {
 						"this is too long a name for a single relation to have",
 						"and would become unwieldy.");
 				else Node::set_new_relation_here(V->next,
-						BinaryPredicates::make_pair_sketchily(explicit_bp_family,
-							WordAssemblages::from_wording(RW), Relation_OtoO));
+						Relations::Explicit::make_pair_sketchily(
+							WordAssemblages::from_wording(RW)));
 				return TRUE;
 			}
 			break;
@@ -343,6 +343,8 @@ void RelationRequests::new(binary_predicate *bp, relation_request *RR) {
 	kind *storage_kind = NULL; /* what kind, if any, might be stored in it */
 	inference_subject *storage_infs = NULL; /* summing these up */
 
+	explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+			
 	int rvno = FALSE, /* relate values not objects? */
 		dynamic = FALSE, /* use dynamic memory allocation for storage? */
 		provide_prn = FALSE, /* allocate the storage property to the kind? */
@@ -355,7 +357,7 @@ void RelationRequests::new(binary_predicate *bp, relation_request *RR) {
 	if (rvno) { bp->relates_values_not_objects = TRUE; bpr->relates_values_not_objects = TRUE; }
 	if (RR->frf) { bp->fast_route_finding = TRUE; bpr->fast_route_finding = TRUE; }
 	if (prn) {
-		bp->i6_storage_property = prn; bpr->i6_storage_property = prn;
+		ED->i6_storage_property = prn;
 		Properties::Valued::set_stored_relation(prn, bp);
 	}
 	if (dynamic) {
@@ -434,8 +436,6 @@ void RelationRequests::new(binary_predicate *bp, relation_request *RR) {
 		}
 	}
 
-	bpr->form_of_relation = bp->form_of_relation;
-
 	LOGIF(RELATION_DEFINITIONS, "Defined the binary predicate:\n$2\n", bp);
 }
 
@@ -526,7 +526,6 @@ omitted from the index.
 	if ((PK) && (Kinds::Behaviour::is_object(PK) == FALSE)) Properties::Valued::set_kind(prn, PK);
 	if (storage_kind) storage_infs = Kinds::Knowledge::as_subject(storage_kind);
 	else storage_infs = NULL;
-//	if (Kinds::Behaviour::is_object(storage_kind) == FALSE) bp->storage_kind = storage_kind;
 	if (((RR->terms[0].unique) || (RR->terms[1].unique)) && (PK) &&
 		(Kinds::Behaviour::is_object(PK) == FALSE))
 		Properties::Valued::now_used_for_non_typesafe_relation(prn);
@@ -552,7 +551,7 @@ Such a relation consumes run-time storage of $5D$ bytes on the Z-machine
 and $14D$ bytes on Glulx, where $D$ is the size of the domain...
 
 @ =
-	bp->form_of_relation = Relation_OtoO;
+	ED->form_of_relation = Relation_OtoO;
 	provide_prn = TRUE;
 	if (Kinds::Behaviour::is_object(storage_kind)) {
 		bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("Relation_Now1to1(*2,%n,*1)", i6_prn_name);
@@ -567,7 +566,7 @@ and $14D$ bytes on Glulx, where $D$ is the size of the domain...
 @ The |Relation_OtoV| case, or one to various: "R relates one K to various K".
 
 @ =
-	bp->form_of_relation = Relation_OtoV;
+	ED->form_of_relation = Relation_OtoV;
 	provide_prn = TRUE;
 	if (Kinds::Behaviour::is_object(storage_kind)) {
 		bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("*2.%n = *1", i6_prn_name);
@@ -582,7 +581,7 @@ and $14D$ bytes on Glulx, where $D$ is the size of the domain...
 @ The |Relation_VtoO| case, or various to one: "R relates various K to one K".
 
 @ =
-	bp->form_of_relation = Relation_VtoO;
+	ED->form_of_relation = Relation_VtoO;
 	provide_prn = TRUE;
 	if (Kinds::Behaviour::is_object(storage_kind)) {
 		bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("*1.%n = *2", i6_prn_name);
@@ -598,7 +597,7 @@ and $14D$ bytes on Glulx, where $D$ is the size of the domain...
 various K".
 
 @ =
-	bp->form_of_relation = Relation_VtoV;
+	ED->form_of_relation = Relation_VtoV;
 	BinaryPredicates::mark_as_needed(bp);
 	bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(Relation_TestVtoV(*1,%n,*2,false))",
 		BinaryPredicates::iname(bp));
@@ -611,7 +610,7 @@ various K".
 another".
 
 @ =
-	bp->form_of_relation = Relation_Sym_OtoO;
+	ED->form_of_relation = Relation_Sym_OtoO;
 	provide_prn = TRUE;
 	if (Kinds::Behaviour::is_object(storage_kind)) {
 		bp->task_functions[NOW_ATOM_TRUE_TASK] = Calculus::Schemas::new("Relation_NowS1to1(*2,%n,*1)", i6_prn_name);
@@ -627,7 +626,7 @@ another".
 to each other".
 
 @ =
-	bp->form_of_relation = Relation_Sym_VtoV;
+	ED->form_of_relation = Relation_Sym_VtoV;
 	BinaryPredicates::mark_as_needed(bp);
 	bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(Relation_TestVtoV(*1,%n,*2,true))",
 		BinaryPredicates::iname(bp));
@@ -640,10 +639,10 @@ to each other".
 other in groups".
 
 @ =
-	bp->form_of_relation = Relation_Equiv;
+	ED->form_of_relation = Relation_Equiv;
 	equivalence_bp_data *D = CREATE(equivalence_bp_data);
 	D->equivalence_partition = NULL;
-	bp->family_specific = STORE_POINTER_equivalence_bp_data(D);
+	ED->equiv_data = D;
 	provide_prn = TRUE;
 	if (Kinds::Behaviour::is_object(storage_kind)) {
 		bp->task_functions[TEST_ATOM_TASK] = Calculus::Schemas::new("(*1.%n == *2.%n)", i6_prn_name, i6_prn_name);
@@ -660,11 +659,12 @@ other in groups".
 	}
 	Properties::Valued::set_kind(prn, K_number);
 
-@ The |Relation_ByRoutine| case, or relation tested by a routine: "R relates
-K to L when (some condition)".
+@ The case of a relation tested by a routine: "R relates K to L when (some
+condition)".
 
 @ =
-	bp->form_of_relation = Relation_ByRoutine;
+	bp->relation_family = by_routine_bp_family;
+	bp->reversal->relation_family = by_routine_bp_family;
 	package_request *P = BinaryPredicates::package(bp);
 	by_routine_bp_data *D = CREATE(by_routine_bp_data);
 	D->condition_defn_text = RR->CONW;
diff --git a/inform7/assertions-module/Chapter 5/Explicit Relations.w b/inform7/assertions-module/Chapter 5/Explicit Relations.w
index edc962a19..da7c08dcc 100644
--- a/inform7/assertions-module/Chapter 5/Explicit Relations.w	
+++ b/inform7/assertions-module/Chapter 5/Explicit Relations.w	
@@ -10,11 +10,15 @@ there are none.
 
 = (early code)
 bp_family *explicit_bp_family = NULL;
+bp_family *by_routine_bp_family = NULL;
 typedef struct explicit_bp_data {
+	int form_of_relation; /* one of the |Relation_*| constants defined below */
+	struct property *i6_storage_property; /* provides run-time storage */
+	struct equivalence_bp_data *equiv_data; /* only used for |Relation_Equiv| */
+	struct inter_name *v2v_bitmap_iname; /* only used for |Relation_VtoV| and |Relation_Sym_VtoV| */
 	CLASS_DEFINITION
 } explicit_bp_data;
 
-
 @ =
 void Relations::Explicit::start(void) {
 	explicit_bp_family = BinaryPredicateFamilies::new();
@@ -23,6 +27,90 @@ void Relations::Explicit::start(void) {
 	METHOD_ADD(explicit_bp_family, SCHEMA_BPF_MTID, Relations::Explicit::REL_compile);
 	METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, Relations::Explicit::REL_describe_for_problems);
 	METHOD_ADD(explicit_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, Relations::Explicit::REL_describe_briefly);
+	by_routine_bp_family = BinaryPredicateFamilies::new();
+	METHOD_ADD(by_routine_bp_family, TYPECHECK_BPF_MTID, Relations::Explicit::REL_typecheck);
+	METHOD_ADD(by_routine_bp_family, ASSERT_BPF_MTID, Relations::Explicit::REL_assert);
+	METHOD_ADD(by_routine_bp_family, SCHEMA_BPF_MTID, Relations::Explicit::REL_compile);
+	METHOD_ADD(by_routine_bp_family, DESCRIBE_FOR_PROBLEMS_BPF_MTID, Relations::Explicit::REL_describe_for_problems);
+	METHOD_ADD(by_routine_bp_family, DESCRIBE_FOR_INDEX_BPF_MTID, Relations::Explicit::REL_br_describe_briefly);
+}
+
+int Relations::Explicit::is_explicit_with_runtime_storage(binary_predicate *bp) {
+	if (bp->relation_family == explicit_bp_family) return TRUE;
+	return TRUE;
+}
+
+@ The following constants are numbered in a way which corresponds to some
+run-time code supporting relations.
+
+@d Relation_Implicit	-1 /* used to mean "none of the below" */
+
+@d Relation_OtoO		1 /* one to one: "R relates one K to one K" */
+@d Relation_OtoV		2 /* one to various: "R relates one K to various K" */
+@d Relation_VtoO		3 /* various to one: "R relates various K to one K" */
+@d Relation_VtoV		4 /* various to various: "R relates various K to various K" */
+@d Relation_Sym_OtoO	5 /* symmetric one to one: "R relates one K to another" */
+@d Relation_Sym_VtoV	6 /* symmetric various to various: "R relates K to each other" */
+@d Relation_Equiv		7 /* equivalence relation: "R relates K to each other in groups" */
+
+@ =
+int Relations::Explicit::allow_arbitrary_assertions(binary_predicate *bp) {
+	int f = Relations::Explicit::get_form_of_relation(bp);
+	if (f == Relation_Equiv) return TRUE;
+	if (f == Relation_VtoV) return TRUE;
+	if (f == Relation_Sym_VtoV) return TRUE;
+	return FALSE;
+}
+
+@ When the source text declares new relations, it turns out to be convenient
+to make their BPs in a two-stage process: to make sketchy, mostly-blank BP
+structures for them early on -- but getting their names registered -- and
+then fill in the correct details later. This is where such sketchy pairs are
+made:
+
+=
+binary_predicate *Relations::Explicit::make_pair_sketchily(word_assemblage wa) {
+	TEMPORARY_TEXT(relname)
+	WRITE_TO(relname, "%V", WordAssemblages::first_word(&wa));
+	binary_predicate *bp =
+		BinaryPredicates::make_pair(explicit_bp_family,
+		BPTerms::new(NULL), BPTerms::new(NULL),
+		relname, NULL, NULL, NULL, wa);
+	DISCARD_TEXT(relname)
+	explicit_bp_data *ED = CREATE(explicit_bp_data);
+	bp->family_specific = STORE_POINTER_explicit_bp_data(ED);
+	bp->reversal->family_specific = STORE_POINTER_explicit_bp_data(ED);
+
+	ED->equiv_data = NULL;
+	ED->i6_storage_property = NULL;
+	ED->form_of_relation = Relation_OtoO;
+	ED->v2v_bitmap_iname = NULL;
+
+	return bp;
+}
+
+property *Relations::Explicit::get_i6_storage_property(binary_predicate *bp) {
+	if (bp->relation_family != explicit_bp_family) return NULL;
+	explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+	return ED->i6_storage_property;
+}
+
+int Relations::Explicit::get_form_of_relation(binary_predicate *bp) {
+	if (bp->relation_family != explicit_bp_family) return Relation_Implicit;
+	explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+	return ED->form_of_relation;
+}
+char *Relations::Explicit::form_to_text(binary_predicate *bp) {
+	switch(Relations::Explicit::get_form_of_relation(bp)) {
+		case Relation_OtoO: return "Relation_OtoO";
+		case Relation_OtoV: return "Relation_OtoV";
+		case Relation_VtoO: return "Relation_VtoO";
+		case Relation_VtoV: return "Relation_VtoV";
+		case Relation_Sym_OtoO: return "Relation_Sym_OtoO";
+		case Relation_Sym_VtoV: return "Relation_Sym_VtoV";
+		case Relation_Equiv: return "Relation_Equiv";
+		default: return "Relation_Implicit";
+	}
 }
 
 @ They typecheck by the default rule only:
@@ -50,13 +138,13 @@ int Relations::Explicit::REL_assert(bp_family *self, binary_predicate *bp,
 		if ((infs0 == NULL) || (infs1 == NULL)) @;
 		if (Relations::Explicit::allow_arbitrary_assertions(bp)) {
 			World::Inferences::draw_relation(bp, infs0, infs1);
-			if ((BinaryPredicates::get_form_of_relation(bp) == Relation_Sym_VtoV) && (infs0 != infs1))
+			if ((Relations::Explicit::get_form_of_relation(bp) == Relation_Sym_VtoV) && (infs0 != infs1))
 				World::Inferences::draw_relation(bp, infs1, infs0);
 			return TRUE;
 		}
-		if (BinaryPredicates::is_explicit_with_runtime_storage(bp)) {
+		if (Relations::Explicit::is_explicit_with_runtime_storage(bp)) {
 			Relations::Explicit::infer_property_based_relation(bp, infs1, infs0);
-			if ((BinaryPredicates::get_form_of_relation(bp) == Relation_Sym_OtoO) && (infs0 != infs1))
+			if ((Relations::Explicit::get_form_of_relation(bp) == Relation_Sym_OtoO) && (infs0 != infs1))
 				Relations::Explicit::infer_property_based_relation(bp, infs0, infs1);
 			return TRUE;
 		}
@@ -64,13 +152,6 @@ int Relations::Explicit::REL_assert(bp_family *self, binary_predicate *bp,
 	return FALSE;
 }
 
-int Relations::Explicit::allow_arbitrary_assertions(binary_predicate *bp) {
-	if (bp->form_of_relation == Relation_Equiv) return TRUE;
-	if (bp->form_of_relation == Relation_VtoV) return TRUE;
-	if (bp->form_of_relation == Relation_Sym_VtoV) return TRUE;
-	return FALSE;
-}
-
 @ This is the point at which non-assertable relations are thrown out.
 
 @ =
@@ -102,12 +183,12 @@ both $R(x,y)$ and $R(x,z)$ will result in contradictory property value
 inferences for $y$ and $z$.
 
 =
-void Relations::Explicit::infer_property_based_relation(binary_predicate *relation,
+void Relations::Explicit::infer_property_based_relation(binary_predicate *bp,
 	inference_subject *infs0, inference_subject *infs1) {
-	if (BinaryPredicates::get_form_of_relation(relation) == Relation_VtoO) {
+	if (Relations::Explicit::get_form_of_relation(bp) == Relation_VtoO) {
 		inference_subject *swap=infs0; infs0=infs1; infs1=swap;
 	}
-	property *prn = BinaryPredicates::get_i6_storage_property(relation);
+	property *prn = Relations::Explicit::get_i6_storage_property(bp);
 	World::Inferences::draw_property(infs0, prn, InferenceSubjects::as_constant(infs1));
 }
 
@@ -125,7 +206,7 @@ int Relations::Explicit::REL_describe_for_problems(bp_family *self, OUTPUT_STREA
 	return FALSE;
 }
 void Relations::Explicit::REL_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
-	switch (bp->form_of_relation) {
+	switch (Relations::Explicit::get_form_of_relation(bp)) {
 		case Relation_OtoO: WRITE("one-to-one"); break;
 		case Relation_OtoV: WRITE("one-to-various"); break;
 		case Relation_VtoO: WRITE("various-to-one"); break;
@@ -133,6 +214,8 @@ void Relations::Explicit::REL_describe_briefly(bp_family *self, OUTPUT_STREAM, b
 		case Relation_Sym_OtoO: WRITE("one-to-another"); break;
 		case Relation_Sym_VtoV: WRITE("various-to-each-other"); break;
 		case Relation_Equiv: WRITE("in groups"); break;
-		case Relation_ByRoutine: WRITE("defined"); break;
 	}
 }
+void Relations::Explicit::REL_br_describe_briefly(bp_family *self, OUTPUT_STREAM, binary_predicate *bp) {
+	WRITE("defined");
+}
diff --git a/inform7/if-module/Chapter 5/Grammar Tokens.w b/inform7/if-module/Chapter 5/Grammar Tokens.w
index 4665ed1f8..25ae26bce 100644
--- a/inform7/if-module/Chapter 5/Grammar Tokens.w	
+++ b/inform7/if-module/Chapter 5/Grammar Tokens.w	
@@ -875,10 +875,10 @@ kind *PL::Parsing::Tokens::compile(gpr_kit *gprk, parse_node *pn, int code_mode,
 					return K_object;
 				}
 			} else {
-				property *prn = BinaryPredicates::get_i6_storage_property(bp);
+				property *prn = Relations::Explicit::get_i6_storage_property(bp);
 				reverse = FALSE;
 				if (BinaryPredicates::is_the_wrong_way_round(bp)) reverse = TRUE;
-				if (BinaryPredicates::get_form_of_relation(bp) == Relation_VtoO) {
+				if (Relations::Explicit::get_form_of_relation(bp) == Relation_VtoO) {
 					if (reverse) reverse = FALSE; else reverse = TRUE;
 				}
 				if (prn) {
diff --git a/inform7/knowledge-module/Chapter 3/Knowledge about Relations.w b/inform7/knowledge-module/Chapter 3/Knowledge about Relations.w
index 06080d059..b78bc7491 100644
--- a/inform7/knowledge-module/Chapter 3/Knowledge about Relations.w	
+++ b/inform7/knowledge-module/Chapter 3/Knowledge about Relations.w	
@@ -22,7 +22,7 @@ void KnowledgeAboutRelations::SUBJ_complete_model(inference_subject *infs) {
 	binary_predicate *bp = InferenceSubjects::as_bp(infs);
 
 	if (BinaryPredicates::store_dynamically(bp)) return; /* handled at run-time instead */
-	if ((BinaryPredicates::get_form_of_relation(bp) == Relation_Equiv) && (bp->right_way_round)) {
+	if ((Relations::Explicit::get_form_of_relation(bp) == Relation_Equiv) && (bp->right_way_round)) {
 		RTRelations::equivalence_relation_make_singleton_partitions(bp, domain_size);
 		inference *i;
 		POSITIVE_KNOWLEDGE_LOOP(i, BinaryPredicates::as_subject(bp), ARBITRARY_RELATION_INF) {
@@ -37,13 +37,10 @@ void KnowledgeAboutRelations::SUBJ_complete_model(inference_subject *infs) {
 
 void KnowledgeAboutRelations::SUBJ_check_model(inference_subject *infs) {
 	binary_predicate *bp = InferenceSubjects::as_bp(infs);
-	if ((bp->right_way_round) &&
-		((bp->form_of_relation == Relation_OtoO) ||
-			(bp->form_of_relation == Relation_Sym_OtoO)))
+	int f = Relations::Explicit::get_form_of_relation(bp);
+	if ((bp->right_way_round) && ((f == Relation_OtoO) || (f == Relation_Sym_OtoO)))
 		KnowledgeAboutRelations::check_OtoO_relation(bp);
-	if ((bp->right_way_round) &&
-		((bp->form_of_relation == Relation_OtoV) ||
-			(bp->form_of_relation == Relation_VtoO)))
+	if ((bp->right_way_round) && ((f == Relation_OtoV) || (f == Relation_VtoO)))
 		KnowledgeAboutRelations::check_OtoV_relation(bp);
 }
 
@@ -77,8 +74,8 @@ void KnowledgeAboutRelations::SUBJ_compile(inference_subject *infs) {
 			}
 			Routines::end(save);
 		} else {
-			if ((bp->form_of_relation == Relation_VtoV) ||
-				(bp->form_of_relation == Relation_Sym_VtoV))
+			int f = Relations::Explicit::get_form_of_relation(bp);
+			if ((f == Relation_VtoV) || (f == Relation_Sym_VtoV))
 				RTRelations::compile_vtov_storage(bp);
 		}
 	}
@@ -102,7 +99,7 @@ void KnowledgeAboutRelations::check_OtoO_relation(binary_predicate *bp) {
 	inference **right_second = (inference **)
 		(Memory::calloc(nc, sizeof(inference *), OBJECT_COMPILATION_MREASON));
 
-	property *prn = BinaryPredicates::get_i6_storage_property(bp);
+	property *prn = Relations::Explicit::get_i6_storage_property(bp);
 
 	inference_subject *infs;
 	LOOP_OVER(infs, inference_subject) right_counts[infs->allocation_id] = 0;
@@ -187,7 +184,7 @@ void KnowledgeAboutRelations::check_OtoV_relation(binary_predicate *bp) {
 		}
 	}
 
-	if (bp->form_of_relation == Relation_VtoO) {
+	if (Relations::Explicit::get_form_of_relation(bp) == Relation_VtoO) {
 		LOOP_OVER(infs, inference_subject) {
 			if (left_counts[infs->allocation_id] >= 2) {
 				StandardProblems::infs_contradiction_problem(_p_(PM_RelationVtoOContradiction),
diff --git a/inform7/runtime-module/Chapter 4/Relations at Run Time.w b/inform7/runtime-module/Chapter 4/Relations at Run Time.w
index fc09fe329..be4c2cc3f 100644
--- a/inform7/runtime-module/Chapter 4/Relations at Run Time.w	
+++ b/inform7/runtime-module/Chapter 4/Relations at Run Time.w	
@@ -2,6 +2,17 @@
 
 Relations need both storage and support code at runtime.
 
+@h The handler.
+
+=
+inter_name *RTRelations::handler_iname(binary_predicate *bp) {
+	if (bp->handler_iname == NULL) {
+		package_request *R = BinaryPredicates::package(bp);
+		bp->handler_iname = Hierarchy::make_iname_in(HANDLER_FN_HL, R);
+	}
+	return bp->handler_iname;
+}
+
 @h Relation records.
 The template layer needs to be able to perform certain actions on any given
 relation, regardless of its mode of storage (if any). We abstract all of this
@@ -99,7 +110,7 @@ void RTRelations::compile_relation_records(void) {
 				DISCARD_TEXT(A)
 			Produce::up(Emit::tree());
 
-			switch(bp->form_of_relation) {
+			switch(Relations::Explicit::get_form_of_relation(bp)) {
 				case Relation_OtoO:
 					Produce::inv_call_iname(Emit::tree(), Hierarchy::find(RELATION_TY_OTOOADJECTIVE_HL));
 					Produce::down(Emit::tree());
@@ -196,7 +207,7 @@ void RTRelations::compile_relation_records(void) {
 		Emit::array_iname_entry(Hierarchy::find(RELS_LOOKUP_ALL_X_HL));
 		Emit::array_iname_entry(RELS_LIST_iname);
 	}
-	switch(dbp->form_of_relation) {
+	switch(Relations::Explicit::get_form_of_relation(dbp)) {
 		case Relation_Implicit:
 			if ((minimal == FALSE) && (BinaryPredicates::can_be_made_true_at_runtime(dbp))) {
 				Emit::array_iname_entry(RELS_ASSERT_TRUE_iname);
@@ -215,7 +226,6 @@ void RTRelations::compile_relation_records(void) {
 		case Relation_Equiv: Emit::array_iname_entry(RELS_EQUIVALENCE_iname); @; break;
 		case Relation_VtoV: @; break;
 		case Relation_Sym_VtoV: Emit::array_iname_entry(RELS_SYMMETRIC_iname); @; break;
-		case Relation_ByRoutine: break;
 		default:
 			internal_error("Binary predicate with unknown structural type");
 	}
@@ -233,29 +243,32 @@ void RTRelations::compile_relation_records(void) {
 @ =
 	binary_predicate *dbp = bp;
 	if (bp->right_way_round == FALSE) dbp = bp->reversal;
-	switch(dbp->form_of_relation) {
-		case Relation_Implicit: /* Field 0 is not used */
-			Emit::array_numeric_entry(0); /* which is not the same as |NULL|, unlike in C */
-			break;
-		case Relation_OtoO:
-		case Relation_OtoV:
-		case Relation_VtoO:
-		case Relation_Sym_OtoO:
-		case Relation_Equiv: /* Field 0 is the property used for run-time storage */
-			Emit::array_iname_entry(Properties::iname(dbp->i6_storage_property));
-			break;
-		case Relation_VtoV:
-		case Relation_Sym_VtoV: /* Field 0 is the bitmap array used for run-time storage */
-			if (dbp->v2v_bitmap_iname == NULL) internal_error("gaah");
-			Emit::array_iname_entry(dbp->v2v_bitmap_iname);
-			break;
-		case Relation_ByRoutine: { /* Field 0 is the routine used to test the relation */
-			by_routine_bp_data *D = RETRIEVE_POINTER_by_routine_bp_data(dbp->family_specific);
-			Emit::array_iname_entry(D->bp_by_routine_iname);
-			break;
+	if (bp->relation_family == by_routine_bp_family) {
+		/* Field 0 is the routine used to test the relation */
+		by_routine_bp_data *D = RETRIEVE_POINTER_by_routine_bp_data(dbp->family_specific);
+		Emit::array_iname_entry(D->bp_by_routine_iname);
+	} else {
+		switch(Relations::Explicit::get_form_of_relation(dbp)) {
+			case Relation_Implicit: /* Field 0 is not used */
+				Emit::array_numeric_entry(0); /* which is not the same as |NULL|, unlike in C */
+				break;
+			case Relation_OtoO:
+			case Relation_OtoV:
+			case Relation_VtoO:
+			case Relation_Sym_OtoO:
+			case Relation_Equiv: /* Field 0 is the property used for run-time storage */
+				Emit::array_iname_entry(
+					Properties::iname(Relations::Explicit::get_i6_storage_property(dbp)));
+				break;
+			case Relation_VtoV:
+			case Relation_Sym_VtoV: {
+				/* Field 0 is the bitmap array used for run-time storage */
+				explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+				if (ED->v2v_bitmap_iname == NULL) internal_error("gaah");
+				Emit::array_iname_entry(ED->v2v_bitmap_iname);
+				break;
+			}
 		}
-		default:
-			internal_error("Binary predicate with unknown structural type");
 	}
 
 @ =
@@ -263,7 +276,7 @@ void RTRelations::compile_relation_records(void) {
 
 @ =
 	TEMPORARY_TEXT(DF)
-	if (bp->form_of_relation == Relation_Implicit)
+	if (Relations::Explicit::get_form_of_relation(bp) == Relation_Implicit)
 		WRITE_TO(DF, "%S", BinaryPredicates::get_log_name(bp));
 	else CompiledText::from_text(DF, Node::get_text(bp->bp_created_at));
 	Emit::array_text_entry(DF);
@@ -277,7 +290,7 @@ void RTRelations::compile_relation_records(void) {
 	binary_predicate *dbp = bp;
 	if (bp->right_way_round == FALSE) { X = I"Y"; Y = I"X"; dbp = bp->reversal; }
 
-	handler = BinaryPredicates::handler_iname(bp);
+	handler = RTRelations::handler_iname(bp);
 	packaging_state save = Routines::begin(handler);
 	inter_symbol *rr_s = LocalVariables::add_named_call_as_symbol(I"rr");
 	inter_symbol *task_s = LocalVariables::add_named_call_as_symbol(I"task");
@@ -366,7 +379,7 @@ void RTRelations::compile_relation_records(void) {
 				}
 				inter_name *shower = NULL;
 				int par = 0;
-				switch(dbp->form_of_relation) {
+				switch(Relations::Explicit::get_form_of_relation(dbp)) {
 					case Relation_OtoO: shower = Hierarchy::find(RELATION_RSHOWOTOO_HL); break;
 					case Relation_OtoV: shower = Hierarchy::find(RELATION_RSHOWOTOO_HL); break;
 					case Relation_VtoO: shower = Hierarchy::find(RELATION_SHOWOTOO_HL); break;
@@ -387,7 +400,7 @@ void RTRelations::compile_relation_records(void) {
 				}
 				inter_name *emptier = NULL;
 				par = 0;
-				switch(dbp->form_of_relation) {
+				switch(Relations::Explicit::get_form_of_relation(dbp)) {
 					case Relation_OtoO: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
 					case Relation_OtoV: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
 					case Relation_VtoO: emptier = Hierarchy::find(RELATION_EMPTYOTOO_HL); break;
@@ -409,7 +422,7 @@ void RTRelations::compile_relation_records(void) {
 				inter_name *router = NULL;
 				int id_flag = TRUE;
 				int follow = FALSE;
-				switch(dbp->form_of_relation) {
+				switch(Relations::Explicit::get_form_of_relation(dbp)) {
 					case Relation_OtoO: router = Hierarchy::find(OTOVRELROUTETO_HL); follow = TRUE; break;
 					case Relation_OtoV: router = Hierarchy::find(OTOVRELROUTETO_HL); follow = TRUE; break;
 					case Relation_VtoO: router = Hierarchy::find(VTOORELROUTETO_HL); follow = TRUE; break;
@@ -697,7 +710,7 @@ void RTRelations::compile_relation_records(void) {
 					Produce::down(Emit::tree());
 						Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
 						Produce::down(Emit::tree());
-							Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+							Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
 							Produce::val_symbol(Emit::tree(), K_value, rr_s);
 							Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
 							if (t == 0) {
@@ -797,7 +810,7 @@ void RTRelations::compile_relation_records(void) {
 					Produce::down(Emit::tree());
 						Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
 						Produce::down(Emit::tree());
-							Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+							Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
 							Produce::val_symbol(Emit::tree(), K_value, rr_s);
 							Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
 							if (t == 0) {
@@ -840,7 +853,7 @@ void RTRelations::compile_relation_records(void) {
 							Produce::down(Emit::tree());
 								Produce::inv_primitive(Emit::tree(), INDIRECT4_BIP);
 								Produce::down(Emit::tree());
-									Produce::val_iname(Emit::tree(), K_value, BinaryPredicates::handler_iname(dbp));
+									Produce::val_iname(Emit::tree(), K_value, RTRelations::handler_iname(dbp));
 									Produce::val_symbol(Emit::tree(), K_value, rr_s);
 									Produce::val_iname(Emit::tree(), K_value, Hierarchy::find(RELS_TEST_HL));
 									Produce::val_symbol(Emit::tree(), K_value, Z1_s);
@@ -990,8 +1003,9 @@ void RTRelations::compile_vtov_storage(binary_predicate *bp) {
 		@;
 
 	package_request *P = BinaryPredicates::package(bp);
-	bp->v2v_bitmap_iname = Hierarchy::make_iname_in(BITMAP_HL, P);
-	packaging_state save = Emit::named_array_begin(bp->v2v_bitmap_iname, K_value);
+	explicit_bp_data *ED = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+	ED->v2v_bitmap_iname = Hierarchy::make_iname_in(BITMAP_HL, P);
+	packaging_state save = Emit::named_array_begin(ED->v2v_bitmap_iname, K_value);
 	@;
 
 	if ((left_count > 0) && (right_count > 0))
@@ -1241,12 +1255,12 @@ $$ p(P) = 12, p(S) = 23, p(R) = 25, p(D) = 26, p(O) = 31. $$
 =
 void RTRelations::equivalence_relation_make_singleton_partitions(binary_predicate *bp,
 	int domain_size) {
-	if (bp->form_of_relation != Relation_Equiv)
+	if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
 		internal_error("attempt to make partition for a non-equivalence relation");
-	equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
+	explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
 	int *partition_array = Memory::calloc(domain_size, sizeof(int), PARTITION_MREASON);
 	for (int i=0; iequivalence_partition = partition_array;
+	D->equiv_data->equivalence_partition = partition_array;
 }
 
 @ The A-parser has meanwhile been reading in facts about the helping relation:
@@ -1282,11 +1296,11 @@ users to set up these relations in a stylistically poor way.
 =
 void RTRelations::equivalence_relation_merge_classes(binary_predicate *bp,
 	int domain_size, int ix1, int ix2) {
-	if (bp->form_of_relation != Relation_Equiv)
+	if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
 		internal_error("attempt to merge classes for a non-equivalence relation");
-	equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
+	explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
 	if (bp->right_way_round == FALSE) bp = bp->reversal;
-	int *partition_array = D->equivalence_partition;;
+	int *partition_array = D->equiv_data->equivalence_partition;;
 	if (partition_array == NULL)
 		internal_error("attempt to use null equivalence partition array");
 	int little, big; /* or, The Fairies' Parliament */
@@ -1325,17 +1339,18 @@ void RTRelations::equivalence_relation_add_properties(binary_predicate *bp) {
 @ =
 	parse_node *val = Rvalues::from_int(
 		RTRelations::equivalence_relation_get_class(bp, infs->allocation_id), EMPTY_WORDING);
-	Properties::Valued::assert(bp->i6_storage_property, infs, val, CERTAIN_CE);
+	Properties::Valued::assert(Relations::Explicit::get_i6_storage_property(bp),
+		infs, val, CERTAIN_CE);
 
 @ Where:
 
 =
 int RTRelations::equivalence_relation_get_class(binary_predicate *bp, int ix) {
-	if (bp->form_of_relation != Relation_Equiv)
+	if (Relations::Explicit::get_form_of_relation(bp) != Relation_Equiv)
 		internal_error("attempt to merge classes for a non-equivalence relation");
 	if (bp->right_way_round == FALSE) bp = bp->reversal;
-	equivalence_bp_data *D = RETRIEVE_POINTER_equivalence_bp_data(bp->family_specific);
-	int *partition_array = D->equivalence_partition;;
+	explicit_bp_data *D = RETRIEVE_POINTER_explicit_bp_data(bp->family_specific);
+	int *partition_array = D->equiv_data->equivalence_partition;;
 	if (partition_array == NULL)
 		internal_error("attempt to use null equivalence partition array");
 	return partition_array[ix];
@@ -1379,7 +1394,7 @@ void RTRelations::compile_defined_relations(void) {
 	RTRelations::compile_relation_records();
 	binary_predicate *bp;
 	LOOP_OVER(bp, binary_predicate)
-		if ((bp->form_of_relation == Relation_ByRoutine) && (bp->right_way_round)) {
+		if ((bp->relation_family == by_routine_bp_family) && (bp->right_way_round)) {
 			current_sentence = bp->bp_created_at;
 			TEMPORARY_TEXT(C)
 			WRITE_TO(C, "Routine to decide if %S(t_0, t_1)", BinaryPredicates::get_log_name(bp));
diff --git a/services/calculus-module/Chapter 3/Binary Predicates.w b/services/calculus-module/Chapter 3/Binary Predicates.w
index dd1fefe06..48f5807c5 100644
--- a/services/calculus-module/Chapter 3/Binary Predicates.w	
+++ b/services/calculus-module/Chapter 3/Binary Predicates.w	
@@ -64,7 +64,6 @@ and "inside" the wrong way round.
 =
 typedef struct binary_predicate {
 	struct bp_family *relation_family;
-	int form_of_relation; /* one of the |Relation_*| constants defined below */
 	struct word_assemblage relation_name; /* (which might have length 0) */
 	struct parse_node *bp_created_at; /* where declared in the source text */
 	struct text_stream *debugging_log_name; /* used when printing propositions to the debug log */
@@ -73,7 +72,6 @@ typedef struct binary_predicate {
 	struct package_request *bp_package;
 	struct inter_name *bp_iname; /* when referred to as a constant */
 	struct inter_name *handler_iname;
-	struct inter_name *v2v_bitmap_iname; /* only relevant for some relations */
 	struct inter_name *initialiser_iname; /* if stored in dynamically allocated memory */
 	#endif
 
@@ -91,10 +89,6 @@ typedef struct binary_predicate {
 
 	/* for optimisation of run-time code: */
 	int dynamic_memory; /* stored in dynamically allocated memory */
-	#ifdef CORE_MODULE
-	struct property *i6_storage_property; /* provides run-time storage */
-	#endif
-	struct kind *storage_kindXXX; /* kind of property owner */
 	int allow_function_simplification; /* allow Inform to make use of any $f_i$ functions? */
 	int fast_route_finding; /* use fast rather than slow route-finding algorithm? */
 	char *loop_parent_optimisation_proviso; /* if not NULL, optimise loops using object tree */
@@ -107,24 +101,6 @@ typedef struct binary_predicate {
 	CLASS_DEFINITION
 } binary_predicate;
 
-@ The following constants are used to identify the "form" of a BP (in that the
-|form_of_relation| field of any BP always equals one of these and never changes).
-These constant names (and values) exactly match a set of constants compiled
-into every I6 program created by Inform, so they can be used freely both in
-the Inform source code and also in the I6 template layer.
-
-@d Relation_Implicit	-1 /* all implicit BPs have this form, and all others are explicit */
-
-@d Relation_OtoO		1 /* one to one: "R relates one K to one K" */
-@d Relation_OtoV		2 /* one to various: "R relates one K to various K" */
-@d Relation_VtoO		3 /* various to one: "R relates various K to one K" */
-@d Relation_VtoV		4 /* various to various: "R relates various K to various K" */
-@d Relation_Sym_OtoO	5 /* symmetric one to one: "R relates one K to another" */
-@d Relation_Sym_VtoV	6 /* symmetric various to various: "R relates K to each other" */
-@d Relation_Equiv		7 /* equivalence relation: "R relates K to each other in groups" */
-
-@d Relation_ByRoutine	8 /* relation tested by a routine: "R relates K to L when (some condition)" */
-
 @ That completes the catalogue of the one-off cases, and we can move on
 to the five families of implicit relations which correspond to other
 structures in the source text.
@@ -257,27 +233,6 @@ binary_predicate *BinaryPredicates::make_pair(bp_family *family,
 	return bp;
 }
 
-@ When the source text declares new relations, it turns out to be convenient
-to make their BPs in a two-stage process: to make sketchy, mostly-blank BP
-structures for them early on -- but getting their names registered -- and
-then fill in the correct details later. This is where such sketchy pairs are
-made:
-
-=
-binary_predicate *BinaryPredicates::make_pair_sketchily(bp_family *family,
-	word_assemblage wa, int f) {
-	TEMPORARY_TEXT(relname)
-	WRITE_TO(relname, "%V", WordAssemblages::first_word(&wa));
-	binary_predicate *bp =
-		BinaryPredicates::make_pair(family,
-		BPTerms::new(NULL), BPTerms::new(NULL),
-		relname, NULL, NULL, NULL, wa);
-	DISCARD_TEXT(relname)
-	bp->form_of_relation = f;
-	bp->reversal->form_of_relation = f;
-	return bp;
-}
-
 @h BP construction.
 The following routine should only ever be called from the two above: provided
 we stick to that, we ensure the golden rule that {\it every BP has a reversal
@@ -296,7 +251,6 @@ binary_predicate *BinaryPredicates::make_single(bp_family *family,
 	i6_schema *mtf, i6_schema *tf, word_assemblage rn) {
 	binary_predicate *bp = CREATE(binary_predicate);
 	bp->relation_family = family;
-	bp->form_of_relation = Relation_Implicit;
 	bp->relation_name = rn;
 	bp->bp_created_at = current_sentence;
 	bp->debugging_log_name = Str::duplicate(name);
@@ -305,7 +259,6 @@ binary_predicate *BinaryPredicates::make_single(bp_family *family,
 	bp->bp_package = NULL;
 	bp->bp_iname = NULL;
 	bp->handler_iname = NULL;
-	bp->v2v_bitmap_iname = NULL;
 	bp->initialiser_iname = NULL;
 	#endif
 
@@ -331,10 +284,6 @@ binary_predicate *BinaryPredicates::make_single(bp_family *family,
 	
 	/* for optimisation of run-time code */
 	bp->dynamic_memory = FALSE;
-	#ifdef CORE_MODULE
-	bp->i6_storage_property = NULL;
-	#endif
-//	bp->storage_kind = NULL;
 	bp->allow_function_simplification = TRUE;
 	bp->fast_route_finding = FALSE;
 	bp->loop_parent_optimisation_proviso = NULL;
@@ -385,18 +334,20 @@ void BinaryPredicates::log_term_details(bp_term_details *bptd, int i) {
 }
 
 void BinaryPredicates::log(binary_predicate *bp) {
-	int i;
 	if (bp == NULL) { LOG("\n"); return; }
+	#ifdef CORE_MODULE
 	LOG("BP%d <%S> - %s way round - %s\n",
 		bp->allocation_id, bp->debugging_log_name, bp->right_way_round?"right":"wrong",
-		BinaryPredicates::form_to_text(bp));
-	for (i=0; i<2; i++) BinaryPredicates::log_term_details(&bp->term_details[i], i);
+		Relations::Explicit::form_to_text(bp));
+	#endif
+	#ifndef CORE_MODULE
+	LOG("BP%d <%S> - %s way round\n",
+		bp->allocation_id, bp->debugging_log_name, bp->right_way_round?"right":"wrong");
+	#endif
+	for (int i=0; i<2; i++) BinaryPredicates::log_term_details(&bp->term_details[i], i);
 	LOG("  test: $i\n", bp->task_functions[TEST_ATOM_TASK]);
 	LOG("  make true: $i\n", bp->task_functions[NOW_ATOM_TRUE_TASK]);
 	LOG("  make false: $i\n", bp->task_functions[NOW_ATOM_FALSE_TASK]);
-	#ifdef CORE_MODULE
-	LOG("  storage property: $Y\n", bp->i6_storage_property);
-	#endif
 }
 
 @h Relation names.
@@ -423,30 +374,6 @@ text_stream *BinaryPredicates::get_log_name(binary_predicate *bp) {
 @h Miscellaneous access routines.
 
 =
-int BinaryPredicates::get_form_of_relation(binary_predicate *bp) {
-	return bp->form_of_relation;
-}
-int BinaryPredicates::is_explicit_with_runtime_storage(binary_predicate *bp) {
-	if (bp->right_way_round == FALSE) bp = bp->reversal;
-	if (bp->form_of_relation == Relation_Implicit) return FALSE;
-	if (bp->form_of_relation == Relation_ByRoutine) return FALSE;
-	return TRUE;
-}
-char *BinaryPredicates::form_to_text(binary_predicate *bp) {
-	switch(bp->form_of_relation) {
-		case Relation_Implicit: return "Relation_Implicit";
-		case Relation_OtoO: return "Relation_OtoO";
-		case Relation_OtoV: return "Relation_OtoV";
-		case Relation_VtoO: return "Relation_VtoO";
-		case Relation_VtoV: return "Relation_VtoV";
-		case Relation_Sym_OtoO: return "Relation_Sym_OtoO";
-		case Relation_Sym_VtoV: return "Relation_Sym_VtoV";
-		case Relation_Equiv: return "Relation_Equiv";
-		case Relation_ByRoutine: return "Relation_ByRoutine";
-		default: return "formless-BP";
-	}
-}
-
 parse_node *BinaryPredicates::get_bp_created_at(binary_predicate *bp) {
 	return bp->bp_created_at;
 }
@@ -508,11 +435,6 @@ TERM_DOMAIN_CALCULUS_TYPE *BinaryPredicates::as_subject(binary_predicate *bp) {
 @ For use when optimising code.
 
 =
-#ifdef CORE_MODULE
-property *BinaryPredicates::get_i6_storage_property(binary_predicate *bp) {
-	return bp->i6_storage_property;
-}
-#endif
 int BinaryPredicates::allows_function_simplification(binary_predicate *bp) {
 	return bp->allow_function_simplification;
 }