From 8ed0da3c1d381919ef0d35823544b009c3d286bb Mon Sep 17 00:00:00 2001 From: Jeremy Wall Date: Fri, 7 Aug 2020 21:16:22 -0400 Subject: [PATCH] Better merging logic handle the cross chapter case --- app/db/src/app/common/bible-reference.spec.ts | 54 ++++++++++++ app/db/src/app/common/bible-reference.ts | 88 ++++++++++++++----- 2 files changed, 120 insertions(+), 22 deletions(-) diff --git a/app/db/src/app/common/bible-reference.spec.ts b/app/db/src/app/common/bible-reference.spec.ts index ccb9a77e..aa5f78d1 100644 --- a/app/db/src/app/common/bible-reference.spec.ts +++ b/app/db/src/app/common/bible-reference.spec.ts @@ -353,4 +353,58 @@ describe('Reference Overlap Detection', () => { expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toEqual(new BibleReference('Gen 1:1-10')); expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); }); + + it('Detects Left chapter Overlaps right chapter', () => { + const leftRef = new BibleReference('Gen 1:1-2:5'); + const rightRef = new BibleReference('Gen 2:1-10'); + + expect(BibleReference.overlap(leftRef, rightRef)).toBe(Overlap.Intersect); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Intersect)).toEqual(new BibleReference('Gen 1:1-2:10')); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toBeNull(); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); + }); + + it('Detects Right chapter Overlaps Left chapter', () => { + const leftRef = new BibleReference('Gen 2:1-10'); + const rightRef = new BibleReference('Gen 1:1-2:5'); + + expect(BibleReference.overlap(leftRef, rightRef)).toBe(Overlap.Intersect); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Intersect)).toEqual(new BibleReference('Gen 1:1-2:10')); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toBeNull(); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); + }); + + it('Detects discontinuous chapters', () => { + const leftRef = new BibleReference('Gen 2:6-10'); + const rightRef = new BibleReference('Gen 1:1-2:5'); + + expect(BibleReference.overlap(leftRef, rightRef)).toBe(Overlap.None); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Intersect)).toBeNull(); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toBeNull(); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); + }); + + it('Detects left side embedded chapters', () => { + const leftRef = new BibleReference('Gen 1:1-2:5'); + const rightRef = new BibleReference('Gen 1:1-3:1'); + + expect(BibleReference.overlap(leftRef, rightRef)).toBe(Overlap.Subset); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Intersect).toString()).toEqual( + new BibleReference('Gen 1:1-3:1').toString() + ); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toEqual(new BibleReference('Gen 1:1-3:1')); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); + }); + + it('Detects right side embedded chapters', () => { + const leftRef = new BibleReference('Gen 1:1-3:1'); + const rightRef = new BibleReference('Gen 1:1-2:5'); + + expect(BibleReference.overlap(leftRef, rightRef)).toBe(Overlap.Subset); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Intersect).toString()).toEqual( + new BibleReference('Gen 1:1-3:1').toString() + ); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.Subset)).toEqual(new BibleReference('Gen 1:1-3:1')); + expect(BibleReference.mergeReference(leftRef, rightRef, Overlap.None)).toBeNull(); + }); }); diff --git a/app/db/src/app/common/bible-reference.ts b/app/db/src/app/common/bible-reference.ts index 599d058c..412edae2 100644 --- a/app/db/src/app/common/bible-reference.ts +++ b/app/db/src/app/common/bible-reference.ts @@ -1885,6 +1885,13 @@ export class BibleReference { return this.Books[booknum]; } + public static locationToIndex(book: number, loc: Location): number { + let ref = book * 100000000; + ref = ref + loc.chapter * 10000; + ref = ref + loc.verse; + return ref; + } + static formatReferenceKey( book: number | string, chapter: number | string, @@ -1899,29 +1906,40 @@ export class BibleReference { return new BibleReference(`${book} ${keyArray[1]}:${keyArray[2]}`); } - static overlap(leftRef: BibleReference, rightRef: BibleReference): Overlap { + public static overlap( + leftRef: BibleReference, + rightRef: BibleReference + ): Overlap { if (leftRef.section.book !== rightRef.section.book) { // either of the above means we are not overlapping return Overlap.None; } - if (leftRef.section.start.chapter === rightRef.section.end.chapter) { - if (leftRef.section.start.verse >= rightRef.section.start.verse) { - if (leftRef.section.end.verse <= rightRef.section.end.verse) { - return Overlap.Subset; - } - return Overlap.Intersect; - } else if (rightRef.section.start.verse > leftRef.section.start.verse) { - if (rightRef.section.end.verse < leftRef.section.end.verse) { - return Overlap.Subset; - } - return Overlap.Intersect; - } + let leftStartIndex = leftRef.startIndex(); + let leftEndIndex = leftRef.endIndex(); + let rightStartIndex = rightRef.startIndex(); + let rightEndIndex = rightRef.endIndex(); + + if ( + // left is subset of right + (leftStartIndex >= rightStartIndex && leftEndIndex <= rightEndIndex) || + // right is subset of left + (rightStartIndex >= leftStartIndex && rightEndIndex <= leftEndIndex) + ) { + return Overlap.Subset; + } + if ( + // left overlaps the right + (leftEndIndex >= rightStartIndex && leftEndIndex <= rightEndIndex) || + // right overlaps the left + (rightEndIndex >= leftStartIndex && rightEndIndex <= leftEndIndex) + ) { + return Overlap.Intersect; } return Overlap.None; } - static mergeReference( + public static mergeReference( ref1: BibleReference, ref2: BibleReference, strategy: Overlap @@ -1950,18 +1968,30 @@ export class BibleReference { ref1.section.start.chapter <= ref2.section.start.chapter ? ref1.section.start.chapter : ref2.section.start.chapter; - mergedRef.section.start.verse = - ref1.section.start.verse <= ref2.section.start.verse - ? ref1.section.start.verse - : ref2.section.start.verse; + if (ref1.section.start.chapter < ref2.section.start.chapter) { + mergedRef.section.start.verse = ref1.section.start.verse; + } else if (ref2.section.start.chapter < ref1.section.start.chapter) { + mergedRef.section.start.verse = ref2.section.start.verse; + } else { + mergedRef.section.start.verse = + ref1.section.start.verse <= ref2.section.start.verse + ? ref1.section.start.verse + : ref2.section.start.verse; + } mergedRef.section.end.chapter = ref1.section.end.chapter >= ref2.section.end.chapter ? ref1.section.end.chapter : ref2.section.end.chapter; - mergedRef.section.end.verse = - ref1.section.end.verse >= ref2.section.end.verse - ? ref1.section.end.verse - : ref2.section.end.verse; + if (ref1.section.end.chapter > ref2.section.end.chapter) { + mergedRef.section.end.verse = ref1.section.end.verse; + } else if (ref2.section.end.chapter > ref1.section.end.chapter) { + mergedRef.section.end.verse = ref2.section.end.verse; + } else { + mergedRef.section.end.verse = + ref1.section.end.verse >= ref2.section.end.verse + ? ref1.section.end.verse + : ref2.section.end.verse; + } return mergedRef; } @@ -2136,6 +2166,20 @@ export class BibleReference { // get the starting book, chapter, verse return BibleReference.toString(this.section); } + + public startIndex(): Number { + return BibleReference.locationToIndex( + this.section.book.bookNumber, + this.section.start + ); + } + + public endIndex(): Number { + return BibleReference.locationToIndex( + this.section.book.bookNumber, + this.section.end + ); + } } class StringUtils {