create procedure DB.DBA.MATINF_SMALLEST_COMMON_SUPERPROPERTIES (
  inout transitive_props any,
  inout supers_of_subs any,
  out all_supers_of_subs any,
  out common_superprops any,
  out all_subs_of_transitives any,
  in only_smallest_common_superprops integer,
  in only_transitive_common_superprops integer )
{
  declare all_supers_of_subs_dict any;
  declare all_subs_of_transitives_dict any;
  declare dirty integer;
  declare pairA_ctr, pairB_ctr, prop_ctr integer;
  declare pairA, pairB, prop any;
-- Step 1: building a transitive closure of supers_of_subs.
  vectorbld_init (all_supers_of_subs);
  all_supers_of_subs_dict := dict_new ();
  pairA_ctr := length (supers_of_subs);
  while (pairA_ctr > 0)
    {
      pairA_ctr := pairA_ctr - 1;
      pairA := supers_of_subs [pairA_ctr];
      if (pairA[0] <> pairA[1])
        {
          dict_put (all_supers_of_subs_dict, pairA, 1);
          vectorbld_acc (all_supers_of_subs, pairA);
        }
    }
  dirty := 1;
  while (dirty)
    {
      dirty := 0;
      pairA_ctr := vectorbld_length (all_supers_of_subs);
      while (pairA_ctr > 0)
        {
          pairA := all_supers_of_subs [pairA_ctr];
          pairA_ctr := pairA_ctr - 1;
          pairB_ctr := vectorbld_length (all_supers_of_subs);
          while (pairB_ctr > 0)
            {
              pairB := all_supers_of_subs [pairB_ctr];
              pairB_ctr := pairB_ctr - 1;
              if (pairA[1] = pairB[0])
                {
                  declare trans any;
                  trans := vector (pairA[0], pairB[1]);
                  if (dict_get (all_supers_of_subs_dict, trans, 0))
                    goto added_already;
                  dict_put (all_supers_of_subs_dict, trans, 1);
                  vectorbld_acc (all_supers_of_subs, trans);
                  dirty := 1;
added_already: ;
                }
            }
        }
    }
  vectorbld_final (all_supers_of_subs);
  -- dbg_obj_princ ('all_supers_of_subs = ', all_supers_of_subs);
-- Step 2: composing common_superprops from pairs of pairs from all_supers_of_subs.
  common_superprops := dict_new ();
  prop_ctr := length (transitive_props);
  while (prop_ctr > 0)
    {
      prop_ctr := prop_ctr - 1;
      prop := transitive_props [prop_ctr];
      dict_put (common_superprops, vector (prop, prop), vector (prop));
    }
  pairA_ctr := length (all_supers_of_subs);
  while (pairA_ctr > 0)
    {
      declare l, r any;
      pairA_ctr := pairA_ctr - 1;
      pairA := all_supers_of_subs [pairA_ctr];
      if (only_transitive_common_superprops and
        not position (pairA[1], transitive_props) )
        goto pair_non_transitive;
      if (pairA[0] < pairA[1])
        { l := pairA[0]; r := pairA[1]; }
      else
        { l := pairA[1]; r := pairA[0]; }
      dict_put (common_superprops, vector (l, r),
        vector_concat (
          dict_get (common_superprops, vector (l, r), vector ()),
          vector (pairA[1]) ) );
pair_non_transitive: ;
    }
  pairA_ctr := length (all_supers_of_subs);
  while (pairA_ctr > 0)
    {
      pairA_ctr := pairA_ctr - 1;
      pairA := all_supers_of_subs [pairA_ctr];
      pairB_ctr := length (all_supers_of_subs);
      while (pairB_ctr > 0)
        {
          pairB_ctr := pairB_ctr - 1;
          pairB := all_supers_of_subs [pairB_ctr];
          -- dbg_obj_princ ('pairA = ', pairA, ', pairB = ', pairB);
          if ((pairA[1] = pairB[1]) and (pairA[0] < pairB[0]))
            {
              declare ordered_subs any;
              declare commons any;
              if (only_transitive_common_superprops and
                not position (pairA[1], transitive_props) )
                goto pair_done;
              ordered_subs := vector (pairA[0], pairB[0]);
              commons := dict_get (common_superprops, ordered_subs, vector());
              if (only_smallest_common_superprops)
                {
                  declare ctr integer;
                  ctr := length (commons);
                  while (ctr > 0)
                    {
                      ctr := ctr - 1;
                      if (dict_get (all_supers_of_subs_dict,
                          vector (commons [ctr], pairA[1]), 0))
                        goto pair_done;
                      else if (dict_get (all_supers_of_subs_dict,
                          vector (pairA[1], commons [ctr]), 0))
                        {
                          commons := vector_concat (
                            subseq (commons, 0, ctr),
                            subseq (commons, ctr + 1) );
                        }
                    }
                }
              commons := vector_concat (commons, vector (pairA[1]));
              dict_put (common_superprops, ordered_subs, commons);
pair_done: ;
            }
        }
    }
-- Step 3. Filling in all_subs_of_transitives.
  all_subs_of_transitives_dict := dict_new ();
  prop_ctr := length (transitive_props);
  while (prop_ctr > 0)
    {
      prop_ctr := prop_ctr - 1;
      prop := transitive_props [prop_ctr];
      if (not dict_get (all_subs_of_transitives_dict, prop, 0))
        dict_put (all_subs_of_transitives_dict, prop, 1);
    }
  pairA_ctr := length (all_supers_of_subs);
  while (pairA_ctr > 0)
    {
      pairA_ctr := pairA_ctr - 1;
      pairA := all_supers_of_subs [pairA_ctr];
      if (position (pairA[1], transitive_props) and not dict_get (all_subs_of_transitives_dict, pairA[0], 0))
        dict_put (all_subs_of_transitives_dict, pairA[0], 1);
    }
  all_subs_of_transitives := dict_list_keys (all_subs_of_transitives_dict, 1);
}
;

create procedure DB.DBA.MATINF_SMALLEST_TRANSITIVE_CLOSURE (
  in src_graph any,
  in dst_graph any,
  inout transitive_props any,
  inout common_superprops any )
{
  declare dirty integer;
  log_enable(0);
  if (not isiri_id (src_graph))
    src_graph := iri_to_id (src_graph, 1);
  if (not isiri_id (dst_graph))
    dst_graph := iri_to_id (dst_graph, 1);
  if (src_graph <> dst_graph)
    {
      for (select S as subj, P as pred, O as obj from DB.DBA.RDF_QUAD where G = src_graph) do
        {
          insert soft DB.DBA.RDF_QUAD (G,S,P,O) values (dst_graph, subj, pred, obj);
          commit work;
        }
    }
  dirty := -1;
  while (dirty)
    {
      -- dbg_obj_princ ('DB.DBA.MATINF_SMALLEST_TRANSITIVE_CLOSURE starts a new round, dirty of previous is ', dirty);
      dirty := 0;
      for (select S as subjB, P as predB, O as objB from DB.DBA.RDF_QUAD where G = dst_graph and position (P, transitive_props) option (QUIETCAST)) do
        {
          for (select S as subjA, P as predA from DB.DBA.RDF_QUAD where G = dst_graph and O=subjB and position (P, transitive_props) option (QUIETCAST)) do
            {
              declare commons any;
              -- verbose: dbg_obj_princ ('A is {', id_to_iri (subjA), ' ', id_to_iri (predA), ' ', id_to_iri (subjB), '}, B is {', id_to_iri (subjB), ' ', id_to_iri (predB), ' ', DB.DBA.RDF_SQLVAL_OF_OBJ(objB), '}');
              if (predA < predB)
                commons := dict_get (common_superprops, vector (predA, predB), null);
              else
                commons := dict_get (common_superprops, vector (predB, predA), null);
              -- verbose: dbg_obj_princ ('commons are ', commons);
              foreach (IRI_ID cp in commons) do
                {
                  declare rc integer;
                  rc := row_count ();
                  -- verbose: dbg_obj_princ ('rc=', rc);
                  insert soft DB.DBA.RDF_QUAD (G,S,P,O) values (dst_graph, subjA, cp, objB);
                  commit work;
                  -- verbose: dbg_obj_princ ('ROWCNT =', row_count ());
                  if (row_count () > rc)
                    {
                      -- verbose: dbg_obj_princ ('A is {', id_to_iri (subjA), ' ', id_to_iri (predA), ' ', id_to_iri (subjB), '}');
                      -- verbose: dbg_obj_princ ('B is {', id_to_iri (subjB), ' ', id_to_iri (predB), ' ', DB.DBA.RDF_SQLVAL_OF_OBJ(objB), '}');
                      -- verbose: dbg_obj_princ ('add {', id_to_iri (subjA), ' ', id_to_iri (cp), ' ', DB.DBA.RDF_SQLVAL_OF_OBJ(objB), '}');
                      dirty := dirty + 1;
                    }
                }
            }
        }
      commit work;
    }
}
;

create procedure DB.DBA.MATINF_OWL_BASED_TRANSITIVE_CLOSURE (
  in owl_graph any,
  in src_graph any,
  in tgt_graph any )
{
  declare transitive_props, supers_of_subs, all_supers_of_subs, common_superprops, all_subs_of_transitives, ses, item any;
  if (not isiri_id (owl_graph))
    owl_graph := iri_to_id (owl_graph, 1);
  transitive_props := (
    select VECTOR_AGG (q."subj")
    from (sparql define output:valmode "LONG"
      prefix owl: <http://www.w3.org/2002/07/owl#>
      prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      select ?subj
      where { graph ?:owl_graph { ?subj a owl:TransitiveProperty } } ) as q);
  -- dbg_obj_princ ('transitive_props = ', transitive_props);
  supers_of_subs := (
    select VECTOR_AGG (vector (q."subj", q."obj"))
    from (sparql define output:valmode "LONG"
      prefix owl: <http://www.w3.org/2002/07/owl#>
      prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
      select ?subj ?obj
      where { graph ?:owl_graph { ?subj rdfs:subPropertyOf ?obj } } ) as q);
  -- dbg_obj_princ ('supers_of_subs = ', supers_of_subs);
  DB.DBA.MATINF_SMALLEST_COMMON_SUPERPROPERTIES (
    transitive_props, supers_of_subs, all_supers_of_subs, common_superprops, all_subs_of_transitives, 0, 1);
  DB.DBA.MATINF_SMALLEST_TRANSITIVE_CLOSURE (src_graph, tgt_graph, all_subs_of_transitives, common_superprops);
}
;
