/// <summary> /// Create a new RelationTable that is the inverse of this relation. /// </summary> /// <remarks> /// Very useful for calculating, for example, pip dependents based on pip dependencies, or /// per-file producers based on per-pip files produced. /// </remarks> public RelationTable <TToId, TFromId> Invert() { RelationTable <TToId, TFromId> result = new RelationTable <TToId, TFromId>(RelatedTable, BaseTableOpt); // We will use result.Values to accumulate the counts as usual. result.SingleValues.Fill(RelatedTable.Count, 0); // And we will use result.m_offsets to store the offsets as usual. result.Offsets.Fill(RelatedTable.Count, 0); int sum = 0; foreach (TFromId id in BaseTableOpt.Ids) { foreach (TToId relatedId in this[id]) { result.SingleValues[relatedId.FromId() - 1]++; sum++; } } // Now we can calculate m_offsets. result.CalculateOffsets(); // And we know the necessary size of m_relations. result.MultiValues.Capacity = sum; result.MultiValues.Fill(sum, default); // Allocate an array of positions to track how many relations we have filled in. SpannableList <int> positions = new SpannableList <int>(RelatedTable.Count + 1); positions.Fill(RelatedTable.Count + 1, 0); // And accumulate all the inverse relations. foreach (TFromId id in BaseTableOpt.Ids) { foreach (TToId relatedId in this[id]) { int relatedIdInt = relatedId.FromId() - 1; int idInt = id.FromId() - 1; int offset = result.Offsets[relatedIdInt]; int position = positions[relatedIdInt]; int relationIndex = result.Offsets[relatedIdInt] + positions[relatedIdInt]; result.MultiValues[relationIndex] = id; positions[relatedIdInt]++; if (positions[relatedIdInt] > result.SingleValues[relatedIdInt]) { // this is a logic bug, should never happen throw new Exception( $"RelationTable.Inverse: logic exception: positions[{relatedIdInt}] = {positions[relatedIdInt]}, result.SingleValues[{relatedIdInt}] = {result.SingleValues[relatedIdInt]}"); } else if (positions[relatedIdInt] == result.SingleValues[relatedIdInt]) { // all the relations for this ID are known. now, we have to sort them. Span <TFromId> finalSpan = result.MultiValues.AsSpan().Slice(result.Offsets[relatedIdInt], result.SingleValues[relatedIdInt]); SpanUtilities.Sort(finalSpan, (id1, id2) => id1.FromId().CompareTo(id2.FromId())); } } } // TODO: error check that there are no zero entries in m_relations return(result); }
/// <summary> /// Hashing. /// </summary> public int GetHashCode([DisallowNull] CharSpan obj) => SpanUtilities.GetHashCode(obj.AsSpan(m_stringTable), CharToInt);