Skip to content

Commit

Permalink
add - Added letter repetition tools
Browse files Browse the repository at this point in the history
---

We've added the letter repetition tools

---

Type: add
Breaking: False
Doc Required: False
Part: 1/1
  • Loading branch information
AptiviCEO committed Apr 9, 2024
1 parent 2e92dcf commit 7ee626e
Show file tree
Hide file tree
Showing 4 changed files with 291 additions and 1 deletion.
153 changes: 153 additions & 0 deletions Textify.Tests/General/TextToolsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Shouldly;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Textify.General;

Expand All @@ -29,6 +31,40 @@ namespace Textify.Tests.General
[TestClass]
public class TextToolsTest
{
public static IEnumerable<object[]> LrpData
{
get
{
return
[
["", TestTools.GetDictionaryFrom([])],
["H", TestTools.GetDictionaryFrom(new() { { 1, 1 } })],
["He", TestTools.GetDictionaryFrom(new() { { 1, 2 }, { 2, 1 } })],
["Hel", TestTools.GetDictionaryFrom(new() { { 1, 3 }, { 2, 3 }, { 3, 1 } })],
["Hell", TestTools.GetDictionaryFrom(new() { { 1, 4 }, { 2, 2 }, { 3, 4 }, { 4, 1 } })],
["Hello", TestTools.GetDictionaryFrom(new() { { 1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 }, { 5, 1 } })],
["Hello!", TestTools.GetDictionaryFrom(new() { { 1, 6 }, { 2, 3 }, { 3, 2 }, { 4, 3 }, { 5, 6 }, { 6, 1 } })],
];
}
}

public static IEnumerable<object[]> LrpDataTwice
{
get
{
return
[
["", TestTools.GetDictionaryFrom([])],
["H", TestTools.GetDictionaryFrom(new() { { 1, 1 }, { 2, 1 } })],
["He", TestTools.GetDictionaryFrom(new() { { 1, 2 }, { 2, 1 }, { 3, 2 }, { 4, 1 } })],
["Hel", TestTools.GetDictionaryFrom(new() { { 1, 3 }, { 2, 3 }, { 3, 1 }, { 4, 3 }, { 5, 3 }, { 6, 1 } })],
["Hell", TestTools.GetDictionaryFrom(new() { { 1, 4 }, { 2, 2 }, { 3, 4 }, { 4, 1 }, { 5, 4 }, { 6, 2 }, { 7, 4 }, { 8, 1 } })],
["Hello", TestTools.GetDictionaryFrom(new() { { 1, 5 }, { 2, 5 }, { 3, 5 }, { 4, 5 }, { 5, 1 }, { 6, 5 }, { 7, 5 }, { 8, 5 }, { 9, 5 }, { 10, 1 } })],
["Hello!", TestTools.GetDictionaryFrom(new() { { 1, 6 }, { 2, 3 }, { 3, 2 }, { 4, 3 }, { 5, 6 }, { 6, 1 }, { 7, 6 }, { 8, 3 }, { 9, 2 }, { 10, 3 }, { 11, 6 }, { 12, 1 } })],
];
}
}

/// <summary>
/// Tests replacing last occurrence of a string
/// </summary>
Expand Down Expand Up @@ -858,6 +894,123 @@ public void TestGetIndexOfEnclosedWordFromIndexSymbols(string text, int idx, int
int actual = text.GetIndexOfEnclosedWordFromIndex(idx, true);
actual.ShouldBe(expected);
}

/// <summary>
/// Tests getting LRP from string
/// </summary>
[TestMethod]
[DataRow("", 1, 0)]
[DataRow("", 2, 0)]
[DataRow("", 3, 0)]
[DataRow("", 10, 0)]
[DataRow("H", 1, 1)]
[DataRow("H", 2, 1)]
[DataRow("He", 1, 2)]
[DataRow("He", 2, 1)]
[DataRow("He", 3, 2)]
[DataRow("He", 4, 1)]
[DataRow("Hel", 1, 3)]
[DataRow("Hel", 2, 3)]
[DataRow("Hel", 3, 1)]
[DataRow("Hel", 4, 3)]
[DataRow("Hel", 5, 3)]
[DataRow("Hel", 6, 1)]
[DataRow("Hell", 1, 4)]
[DataRow("Hell", 2, 2)]
[DataRow("Hell", 3, 4)]
[DataRow("Hell", 4, 1)]
[DataRow("Hell", 5, 4)]
[DataRow("Hell", 6, 2)]
[DataRow("Hell", 7, 4)]
[DataRow("Hell", 8, 1)]
[DataRow("Hello", 1, 5)]
[DataRow("Hello", 2, 5)]
[DataRow("Hello", 3, 5)]
[DataRow("Hello", 4, 5)]
[DataRow("Hello", 5, 1)]
[DataRow("Hello", 6, 5)]
[DataRow("Hello", 7, 5)]
[DataRow("Hello", 8, 5)]
[DataRow("Hello", 9, 5)]
[DataRow("Hello", 10, 1)]
[DataRow("Hello!", 1, 6)]
[DataRow("Hello!", 2, 3)]
[DataRow("Hello!", 3, 2)]
[DataRow("Hello!", 4, 3)]
[DataRow("Hello!", 5, 6)]
[DataRow("Hello!", 6, 1)]
[DataRow("Hello!", 7, 6)]
[DataRow("Hello!", 8, 3)]
[DataRow("Hello!", 9, 2)]
[DataRow("Hello!", 10, 3)]
[DataRow("Hello!", 11, 6)]
[DataRow("Hello!", 12, 1)]
[Description("Querying")]
public void TestLRP(string target, int lrpSteps, int expected)
{
int lrp = target.GetLetterRepetitionPattern(lrpSteps);
lrp.ShouldBe(expected);
}

/// <summary>
/// Tests getting LRP table from string
/// </summary>
[TestMethod]
[DynamicData(nameof(LrpData))]
[Description("Querying")]
public void TestLRPTable(string target, ReadOnlyDictionary<int, int> expected)
{
var lrp = target.GetLetterRepetitionPatternTable();
lrp.ShouldBe(expected);
}

/// <summary>
/// Tests getting LRP table from string (twice)
/// </summary>
[TestMethod]
[DynamicData(nameof(LrpDataTwice))]
[Description("Querying")]
public void TestLRPTableTwice(string target, ReadOnlyDictionary<int, int> expected)
{
var lrp = target.GetLetterRepetitionPatternTable(true);
lrp.ShouldBe(expected);
}

/// <summary>
/// Tests getting list of repeated letters without wiping the single letter occurrences
/// </summary>
[TestMethod]
[Description("Querying")]
public void TestGetListOfRepeatedLettersNoWipe()
{
var repeated = "Hello!".GetListOfRepeatedLetters();
repeated.ShouldContainKey('H');
repeated['H'].ShouldBe(1);
repeated.ShouldContainKey('e');
repeated['e'].ShouldBe(1);
repeated.ShouldContainKey('l');
repeated['l'].ShouldBe(2);
repeated.ShouldContainKey('o');
repeated['o'].ShouldBe(1);
repeated.ShouldContainKey('!');
repeated['!'].ShouldBe(1);
}

/// <summary>
/// Tests getting list of repeated letters without wiping the single letter occurrences
/// </summary>
[TestMethod]
[Description("Querying")]
public void TestGetListOfRepeatedLettersWipe()
{
var repeated = "Hello!".GetListOfRepeatedLetters(true);
repeated.ShouldNotContainKey('H');
repeated.ShouldNotContainKey('e');
repeated.ShouldContainKey('l');
repeated['l'].ShouldBe(2);
repeated.ShouldNotContainKey('o');
repeated.ShouldNotContainKey('!');
}
}

}
30 changes: 30 additions & 0 deletions Textify.Tests/TestTools.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Textify Copyright (C) 2023-2024 Aptivi
//
// This file is part of Textify
//
// Textify is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Textify is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY, without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//

using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Textify.Tests
{
internal static class TestTools
{
internal static ReadOnlyDictionary<int, int> GetDictionaryFrom(Dictionary<int, int> dict) =>
new(dict);
}
}
2 changes: 1 addition & 1 deletion Textify.Tests/Textify.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand Down
107 changes: 107 additions & 0 deletions Textify/General/TextTools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
Expand Down Expand Up @@ -860,5 +861,111 @@ public static int GetIndexOfEnclosedWordFromIndex(this string target, int source
idx = 0;
return idx;
}

/// <summary>
/// Gets the letter repetition pattern (LRP) that determines how many times the program needs to step "<paramref name="steps"/>" times on the string until it reaches the end of the string.
/// </summary>
/// <param name="target">Target string</param>
/// <param name="steps">Number of steps</param>
public static int GetLetterRepetitionPattern(this string target, int steps)
{
if (target is null)
throw new TextifyException("The target may not be null");
if (steps <= 0)
throw new TextifyException("Can't get the letter repetition pattern with zero or negative number of steps.");
if (target.Length == 0)
return 0;

// Some variables
int length = target.Length;
int lastPosition = 0;
int repeatTimes = 0;

// The LRP formula requires a loop
while (true)
{
// Loop through the number of steps with the last position
int step = 0;
for (int i = lastPosition; i <= steps + lastPosition; i++)
{
step = i;

// Check to see if the number of steps exceeds the string length
while (step > length)
step -= length;
}

// Now, update the last position and increment the repeat times by one
lastPosition = step;
repeatTimes += 1;

// If we're at the end of the string, return the repeat times
if (lastPosition == length)
return repeatTimes;
}
}

/// <summary>
/// Gets the letter repetition pattern (LRP) table from the length of the string
/// </summary>
/// <param name="target">Target string</param>
/// <param name="twice">Whether to add the LRP of the string using the number of steps up to twice the string length or not</param>
public static ReadOnlyDictionary<int, int> GetLetterRepetitionPatternTable(this string target, bool twice = false)
{
if (target is null)
throw new TextifyException("The target may not be null");
if (target.Length == 0)
return new(new Dictionary<int, int>());

// Now, iterate through the target length
Dictionary<int, int> lrpTable = [];
int targetLength = target.Length * (twice ? 2 : 1);
for (int i = 1; i <= targetLength; i++)
{
int lrp = target.GetLetterRepetitionPattern(i);
lrpTable.Add(i, lrp);
}

// Return the resulting dictionary
return new(lrpTable);
}

/// <summary>
/// Gets the list of repeated letters, including those that have occurred only once
/// </summary>
/// <param name="target">Target string</param>
/// <param name="removeSingle">Whether to remove all characters that only contain a single occurrence or not</param>
public static ReadOnlyDictionary<char, int> GetListOfRepeatedLetters(this string target, bool removeSingle = false)
{
if (target is null)
throw new TextifyException("The target may not be null");
if (target.Length == 0)
return new(new Dictionary<char, int>());

// Add repeated letters to the list
var letters = new Dictionary<char, int>();
foreach (char chr in target)
{
if (letters.ContainsKey(chr))
letters[chr]++;
else
letters.Add(chr, 1);
}

// Remove a letter that only occurred once
if (removeSingle)
{
for (int i = letters.Count - 1; i >= 0; i--)
{
char character = letters.Keys.ElementAt(i);
int repetitions = letters[character];
if (repetitions == 1)
letters.Remove(character);
}
}

// Return the resulting dictionary
return new(letters);
}
}
}

0 comments on commit 7ee626e

Please sign in to comment.