From bf4416e4fce4557bee0c15f9dc8f2d10f8122fac Mon Sep 17 00:00:00 2001 From: Enkidu93 Date: Tue, 29 Sep 2026 16:27:45 -0400 Subject: [PATCH 1/5] Add IsEquivalentTo versification extension method; only convert USFM when versifications are not equivalent --- .../Corpora/ParatextProjectTextUpdaterBase.cs | 2 +- src/SIL.Machine/Corpora/ScrVersExtensions.cs | 31 ++++++++++++ .../Corpora/ScrVersExtensionsTests.cs | 50 +++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/SIL.Machine/Corpora/ParatextProjectTextUpdaterBase.cs b/src/SIL.Machine/Corpora/ParatextProjectTextUpdaterBase.cs index 1289f3f4c..3445dde68 100644 --- a/src/SIL.Machine/Corpora/ParatextProjectTextUpdaterBase.cs +++ b/src/SIL.Machine/Corpora/ParatextProjectTextUpdaterBase.cs @@ -68,7 +68,7 @@ public string UpdateUsfm( ScrVers rowsVersification = UpdateUsfmParserHandler.GetRowsVersification(rows); ScrVers parseVersification = _settings.Versification; - if (rowsVersification != _settings.Versification) + if (!rowsVersification.IsEquivalentTo(_settings.Versification)) { var converter = new ConvertUsfmVersificationHandler(rowsVersification); UsfmParser.Parse(tokens, converter, _settings.Stylesheet, _settings.Versification); diff --git a/src/SIL.Machine/Corpora/ScrVersExtensions.cs b/src/SIL.Machine/Corpora/ScrVersExtensions.cs index 9fae902d4..1f2b5c557 100644 --- a/src/SIL.Machine/Corpora/ScrVersExtensions.cs +++ b/src/SIL.Machine/Corpora/ScrVersExtensions.cs @@ -1,4 +1,6 @@ using System.Collections.Generic; +using System.Linq; +using SIL.Extensions; using SIL.Scripture; namespace SIL.Machine.Corpora @@ -58,5 +60,34 @@ public static bool HasCrossBookMappings(this ScrVers scrVers, ScrVers referenceV } return false; } + + public static bool IsEquivalentTo(this ScrVers scrVers, ScrVers other) + { + if (scrVers.Equals(other)) + return true; + + // If all verses in the versifications are 1) equal (accounts for mapping) + // and 2) graphically identical in regard to book, chapter, and verse, then the versifications are equivalent + foreach ( + (VerseRef thisVerse, VerseRef otherVerse) in scrVers + .AllIncludedVerses() + .Zip(other.AllIncludedVerses()) + .Select(tup => (tup.Item1, tup.Item2)) + ) + { + if ( + !( + thisVerse.ChangeVersificationWithSegments(otherVerse.Versification).Equals(otherVerse) + && thisVerse.VerseNum == otherVerse.VerseNum + && thisVerse.ChapterNum == otherVerse.ChapterNum + && thisVerse.BookNum == otherVerse.BookNum + ) + ) + { + return false; + } + } + return true; + } } } diff --git a/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs b/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs index ea5caba92..738bf941f 100644 --- a/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs +++ b/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs @@ -43,4 +43,54 @@ public void HasCrossBookMappings() Assert.That(ScrVers.Vulgate.HasCrossBookMappings(ScrVers.English)); } } + + [Test] + public void IsEquivalent() + { + ScrVers customVrs1; + using (CorporaUtils.VersificationLock.Lock()) + { + string src = "MAT 1:2 = MAT 1:1\nMAT 1:1 = MAT 1:2\n"; + using var reader = new StringReader(src); + customVrs1 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + Versification.Table.Implementation.RemoveAllUnknownVersifications(); + } + + ScrVers customVrs2; + using (CorporaUtils.VersificationLock.Lock()) + { + string src = "MAT 1:1 = MAT 1:2\nMAT 1:2 = MAT 1:1\n"; + using var reader = new StringReader(src); + customVrs2 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + Versification.Table.Implementation.RemoveAllUnknownVersifications(); + } + + ScrVers customVrs3; + using (CorporaUtils.VersificationLock.Lock()) + { + string src = "MAT 1:1 = MAT 1:2\n# This is a comment\nMAT 1:2 = MAT 1:1\nMAT 1:2 = MAT 1:1\n"; + using var reader = new StringReader(src); + customVrs3 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + Versification.Table.Implementation.RemoveAllUnknownVersifications(); + } + + ScrVers customVrs4; + using (CorporaUtils.VersificationLock.Lock()) + { + string src = "&MAT 1:2-3 = MAT 1:2\nMAT 1:4 = MAT 1:3\n"; + using var reader = new StringReader(src); + customVrs4 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + Versification.Table.Implementation.RemoveAllUnknownVersifications(); + } + + using (Assert.EnterMultipleScope()) + { + Assert.That(ScrVers.English.IsEquivalentTo(ScrVers.English)); + Assert.That(!ScrVers.English.IsEquivalentTo(ScrVers.Original)); + Assert.That(customVrs1.IsEquivalentTo(customVrs1)); + Assert.That(customVrs1.IsEquivalentTo(customVrs2)); + Assert.That(customVrs1.IsEquivalentTo(customVrs3)); + Assert.That(!customVrs1.IsEquivalentTo(customVrs4)); + } + } } From 2a4010999550bb5a036d1927ba2b8c0c871ff4d2 Mon Sep 17 00:00:00 2001 From: Enkidu93 Date: Wed, 30 Sep 2026 10:54:55 -0400 Subject: [PATCH 2/5] Address reviewer comments --- src/SIL.Machine/Corpora/ScrVersExtensions.cs | 8 ++--- .../Corpora/ScrVersExtensionsTests.cs | 32 +++++++++++++------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/SIL.Machine/Corpora/ScrVersExtensions.cs b/src/SIL.Machine/Corpora/ScrVersExtensions.cs index 1f2b5c557..9a3cea324 100644 --- a/src/SIL.Machine/Corpora/ScrVersExtensions.cs +++ b/src/SIL.Machine/Corpora/ScrVersExtensions.cs @@ -77,10 +77,10 @@ public static bool IsEquivalentTo(this ScrVers scrVers, ScrVers other) { if ( !( - thisVerse.ChangeVersificationWithSegments(otherVerse.Versification).Equals(otherVerse) - && thisVerse.VerseNum == otherVerse.VerseNum - && thisVerse.ChapterNum == otherVerse.ChapterNum - && thisVerse.BookNum == otherVerse.BookNum + thisVerse + .ChangeVersificationWithSegments(ScrVers.Original) + .Equals(otherVerse.ChangeVersificationWithSegments(ScrVers.Original)) + && thisVerse.BBBCCCVVVS == otherVerse.BBBCCCVVVS ) ) { diff --git a/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs b/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs index 738bf941f..54644b04d 100644 --- a/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs +++ b/tests/SIL.Machine.Tests/Corpora/ScrVersExtensionsTests.cs @@ -45,41 +45,51 @@ public void HasCrossBookMappings() } [Test] - public void IsEquivalent() + public void IsEquivalentTo() { ScrVers customVrs1; using (CorporaUtils.VersificationLock.Lock()) { - string src = "MAT 1:2 = MAT 1:1\nMAT 1:1 = MAT 1:2\n"; + string src = "MAT 1:2 = MAT 1:1\nMAT 1:1 = MAT 1:2\n-EXO 25:6\n-EXO 28:23"; using var reader = new StringReader(src); - customVrs1 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + customVrs1 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom1"); Versification.Table.Implementation.RemoveAllUnknownVersifications(); } ScrVers customVrs2; using (CorporaUtils.VersificationLock.Lock()) { - string src = "MAT 1:1 = MAT 1:2\nMAT 1:2 = MAT 1:1\n"; + string src = "MAT 1:1 = MAT 1:2\nMAT 1:2 = MAT 1:1\n-EXO 28:23\n-EXO 25:6"; using var reader = new StringReader(src); - customVrs2 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + customVrs2 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom2"); Versification.Table.Implementation.RemoveAllUnknownVersifications(); } ScrVers customVrs3; using (CorporaUtils.VersificationLock.Lock()) { - string src = "MAT 1:1 = MAT 1:2\n# This is a comment\nMAT 1:2 = MAT 1:1\nMAT 1:2 = MAT 1:1\n"; + string src = + "MAT 1:1 = MAT 1:2\n# This is a comment\nMAT 1:2 = MAT 1:1\nMAT 1:2 = MAT 1:1\n-EXO 25:6\n-EXO 28:23"; using var reader = new StringReader(src); - customVrs3 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + customVrs3 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom3"); Versification.Table.Implementation.RemoveAllUnknownVersifications(); } ScrVers customVrs4; using (CorporaUtils.VersificationLock.Lock()) { - string src = "&MAT 1:2-3 = MAT 1:2\nMAT 1:4 = MAT 1:3\n"; + string src = "&MAT 1:2-3 = MAT 1:2\nMAT 1:4 = MAT 1:3\n-EXO 25:6\n-EXO 28:23"; using var reader = new StringReader(src); - customVrs4 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom"); + customVrs4 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom4"); + Versification.Table.Implementation.RemoveAllUnknownVersifications(); + } + + ScrVers customVrs5; + using (CorporaUtils.VersificationLock.Lock()) + { + string src = ""; + using var reader = new StringReader(src); + customVrs5 = Versification.Table.Implementation.Load(reader, "vers.txt", ScrVers.English, "custom5"); Versification.Table.Implementation.RemoveAllUnknownVersifications(); } @@ -87,10 +97,14 @@ public void IsEquivalent() { Assert.That(ScrVers.English.IsEquivalentTo(ScrVers.English)); Assert.That(!ScrVers.English.IsEquivalentTo(ScrVers.Original)); + Assert.That(ScrVers.Original.IsEquivalentTo(ScrVers.Original)); + Assert.That(!ScrVers.Original.IsEquivalentTo(ScrVers.English)); Assert.That(customVrs1.IsEquivalentTo(customVrs1)); Assert.That(customVrs1.IsEquivalentTo(customVrs2)); Assert.That(customVrs1.IsEquivalentTo(customVrs3)); Assert.That(!customVrs1.IsEquivalentTo(customVrs4)); + Assert.That(!customVrs1.IsEquivalentTo(customVrs5)); + Assert.That(customVrs5.IsEquivalentTo(ScrVers.English)); } } } From d5c01b8014b232de885ef780c1a08b1cf4d24081 Mon Sep 17 00:00:00 2001 From: Enkidu93 Date: Thu, 1 Oct 2026 14:38:12 -0400 Subject: [PATCH 3/5] Address reviewer comments --- src/SIL.Machine/Corpora/ScrVersExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/SIL.Machine/Corpora/ScrVersExtensions.cs b/src/SIL.Machine/Corpora/ScrVersExtensions.cs index 9a3cea324..87339aec4 100644 --- a/src/SIL.Machine/Corpora/ScrVersExtensions.cs +++ b/src/SIL.Machine/Corpora/ScrVersExtensions.cs @@ -71,7 +71,8 @@ public static bool IsEquivalentTo(this ScrVers scrVers, ScrVers other) foreach ( (VerseRef thisVerse, VerseRef otherVerse) in scrVers .AllIncludedVerses() - .Zip(other.AllIncludedVerses()) + .Concat(new VerseRef()) + .Zip(other.AllIncludedVerses().Concat(new VerseRef())) .Select(tup => (tup.Item1, tup.Item2)) ) { From ac247f1ea4097b0b8ddfa819d024edda3a421976 Mon Sep 17 00:00:00 2001 From: Enkidu93 Date: Mon, 5 Oct 2026 10:33:06 -0400 Subject: [PATCH 4/5] Add using --- src/SIL.Machine/Corpora/ScrVersExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SIL.Machine/Corpora/ScrVersExtensions.cs b/src/SIL.Machine/Corpora/ScrVersExtensions.cs index 87339aec4..620074a68 100644 --- a/src/SIL.Machine/Corpora/ScrVersExtensions.cs +++ b/src/SIL.Machine/Corpora/ScrVersExtensions.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using SIL.Extensions; @@ -73,7 +74,6 @@ public static bool IsEquivalentTo(this ScrVers scrVers, ScrVers other) .AllIncludedVerses() .Concat(new VerseRef()) .Zip(other.AllIncludedVerses().Concat(new VerseRef())) - .Select(tup => (tup.Item1, tup.Item2)) ) { if ( From 89a074f6a671635d3a7e9307df37d26c11426734 Mon Sep 17 00:00:00 2001 From: Enkidu93 Date: Mon, 5 Oct 2026 14:33:08 -0400 Subject: [PATCH 5/5] Remove accidentally committed change --- .../ConvertUsfmVersificationHandlerTests.cs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/tests/SIL.Machine.Tests/Corpora/ConvertUsfmVersificationHandlerTests.cs b/tests/SIL.Machine.Tests/Corpora/ConvertUsfmVersificationHandlerTests.cs index 12e48867f..3c9bbe4b5 100644 --- a/tests/SIL.Machine.Tests/Corpora/ConvertUsfmVersificationHandlerTests.cs +++ b/tests/SIL.Machine.Tests/Corpora/ConvertUsfmVersificationHandlerTests.cs @@ -974,24 +974,10 @@ private static void AssertUsfmEquals(string target, string truth) Assert.That(target, Is.Not.Null); string[] targetLines = target.Split('\n'); string[] truthLines = truth.Split('\n'); - // Assert.That(targetLines.Length, Is.EqualTo(truthLines.Length)); + Assert.That(targetLines, Has.Length.EqualTo(truthLines.Length)); for (int i = 0; i < truthLines.Length; i++) { - Assert.That( - targetLines[i].Trim(), - Is.EqualTo(truthLines[i].Trim()), - message: string.Join( - "\n", - [ - "Expected vs. \n\tActual", - .. truthLines - .Zip(targetLines) - .Select(pair => - $"\n{pair.First}\n\t{(pair.First.Trim() != pair.Second.Trim() ? "***" : "")}{pair.Second}" - ), - ] - ) - ); + Assert.That(targetLines[i].Trim(), Is.EqualTo(truthLines[i].Trim()), message: $"Line {i}"); } } }