diff --git a/MarkdownSpec.md b/MarkdownSpec.md
index 886e99c95..c1e161b46 100644
--- a/MarkdownSpec.md
+++ b/MarkdownSpec.md
@@ -70,4 +70,27 @@ __Непарные_ символы в рамках одного абзаца н
превратится в:
-\
Заголовок \с \разными\ символами\\
\ No newline at end of file
+\Заголовок \с \разными\ символами\\
+
+# Ссылки
+
+Формат: [Название ссылки](Ссылка)
+
+Текст помещенный в квадратные скобки является названием ссылки.
+Это название будет видно пользователю при взаимодействии с ссылками, вместо самой ссылки.
+
+В круглые скобки помещается сама ссылка, которая будет скрываться за именем.
+
+Преобразованный вид: Название ссылки
+Ключевое словов href обозначает начало ссылки, это слово помещается внутри открывающегося тэга <а>.
+Далее идет навзание ссылки и затем закрывающийся тэг .
+
+В названии ссылки могут использоваться жирный и курсивный тэг.
+[__Жирный выглядит так__](https://example.com)
+[_Курсив выглядит так_](https://example.com)
+
+Ссылка должна иметь имя, чтобы корректно отображаться.
+[](Это не ссылка)
+
+В отличие от этого имя может не иметь ссылки и это будет корректно.
+[Например так]()
\ No newline at end of file
diff --git a/cs/Chess/ChessProblem.cs b/cs/Chess/ChessProblem.cs
index f0d6ab430..fe56611a0 100644
--- a/cs/Chess/ChessProblem.cs
+++ b/cs/Chess/ChessProblem.cs
@@ -1,4 +1,6 @@
-namespace Chess
+using System.Drawing;
+
+namespace Chess
{
public class ChessProblem
{
@@ -13,21 +15,22 @@ public static void LoadFrom(string[] lines)
// Определяет мат, шах или пат белым.
public static void CalculateChessStatus()
{
- var isCheck = IsCheckForWhite();
+ var isCheck = IsCheck(PieceColor.White);
var hasMoves = false;
foreach (var locFrom in board.GetPieces(PieceColor.White))
{
foreach (var locTo in board.GetPiece(locFrom).GetMoves(locFrom, board))
{
- var old = board.GetPiece(locTo);
- board.Set(locTo, board.GetPiece(locFrom));
- board.Set(locFrom, null);
- if (!IsCheckForWhite())
+ var previousPosition = board.GetPiece(locTo);
+ using var tempMove = board.PerformTemporaryMove(locFrom, locTo);
+ if (!IsCheck(PieceColor.White))
hasMoves = true;
- board.Set(locFrom, board.GetPiece(locTo));
- board.Set(locTo, old);
}
}
+ SetChessStatus(isCheck, hasMoves);
+ }
+
+ private static void SetChessStatus(bool isCheck, bool hasMoves){
if (isCheck)
if (hasMoves)
ChessStatus = ChessStatus.Check;
@@ -37,21 +40,18 @@ public static void CalculateChessStatus()
}
// check — это шах
- private static bool IsCheckForWhite()
+ private static bool IsCheck(PieceColor color)
{
- var isCheck = false;
- foreach (var loc in board.GetPieces(PieceColor.Black))
+ var oppositeColor = (color == PieceColor.White) ? PieceColor.Black : PieceColor.White;
+ foreach (var locFrom in board.GetPieces(oppositeColor))
{
- var piece = board.GetPiece(loc);
- var moves = piece.GetMoves(loc, board);
- foreach (var destination in moves)
+ foreach (var locTo in board.GetPiece(locFrom).GetMoves(locFrom, board))
{
- if (Piece.Is(board.GetPiece(destination),
- PieceColor.White, PieceType.King))
- isCheck = true;
+ if (Piece.Is(board.GetPiece(locTo),
+ color, PieceType.King))
+ return true;
}
}
- if (isCheck) return true;
return false;
}
}
diff --git a/cs/ControlDigit/ControlDigit.csproj b/cs/ControlDigit/ControlDigit.csproj
index 863b30396..5177d2fff 100644
--- a/cs/ControlDigit/ControlDigit.csproj
+++ b/cs/ControlDigit/ControlDigit.csproj
@@ -1,15 +1,23 @@
- 8
+ 6
net6
false
-
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
-
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
diff --git a/cs/ControlDigit/Snils/SnilsExtensions.cs b/cs/ControlDigit/Snils/SnilsExtensions.cs
index e4dd90326..402cee909 100644
--- a/cs/ControlDigit/Snils/SnilsExtensions.cs
+++ b/cs/ControlDigit/Snils/SnilsExtensions.cs
@@ -1,12 +1,65 @@
using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using NUnit.Framework.Internal.Execution;
+using System.Linq;
namespace ControlDigit
{
public static class SnilsExtensions
{
- public static int CalculateSnils(this long number)
- {
- throw new NotImplementedException();
+ public static int CalculateSnils(this long number){
+ var dict = NumberDecompotion(number);
+ var M = SummDecompotion(dict);
+ var result = MakeControlDigit(M);
+ return result;
+ }
+
+ private static Dictionary NumberDecompotion(this long number){
+ var counter = 1;
+ var dict = new Dictionary();
+ while(number / 10 >= 1){
+ dict.Add(counter, (int)number % 10);
+ counter++;
+ number = number / 10;
+ }
+ dict.Add(counter, (int)number % 10);
+ return dict;
+ }
+
+ private static int SummDecompotion(Dictionary dict){
+ var summ = 0;
+ foreach(var pair in dict){
+ summ += pair.Key * pair.Value;
+ }
+ return summ;
+ }
+
+ private static IEnumerable MakeFactorsSnils(int length){
+ return Enumerable.Range(1, length);
+ }
+
+ private static IEnumerable MakeFactorsUpc(int length){
+ return Enumerable.Range(1, length);
+ }
+
+ private static int CountControlDigit(IEnumerable values, Func> evaluateFactors, int length){
+ var factors = evaluateFactors(length);
+ var summ = 0;
+ //for(int i = 0;i < length;i++){
+ //summ += factors[i] * values[i];
+ //}
+ var ggygu = factors.Zip(values)
+ }
+
+ private static int MakeControlDigit(int summ){
+ if (summ > 101)
+ summ = summ % 101;
+ if (summ < 100)
+ return summ;
+ else
+ return 0;
}
}
}
diff --git a/cs/ControlDigit/Upc/UpcExtensions.cs b/cs/ControlDigit/Upc/UpcExtensions.cs
index d1c376330..b55f6443f 100644
--- a/cs/ControlDigit/Upc/UpcExtensions.cs
+++ b/cs/ControlDigit/Upc/UpcExtensions.cs
@@ -6,7 +6,16 @@ public static class UpcExtensions
{
public static int CalculateUpc(this long number)
{
- throw new NotImplementedException();
+ var numberToString = number.ToString();
+ var summ = 0;
+ for(int i = numberToString.Length - 1;i>=0;i-=2){
+ summ += int.Parse(numberToString[i].ToString()) * 3;
+ }
+ for(int i = numberToString.Length - 2;i>=0;i-=2){
+ summ += int.Parse(numberToString[i].ToString());
+ }
+ var M = (summ % 10 == 0)? 0: 10 - summ % 10;
+ return M;
}
}
}
diff --git a/cs/Markdown/HelperFunctions.cs b/cs/Markdown/HelperFunctions.cs
new file mode 100644
index 000000000..63aa3f920
--- /dev/null
+++ b/cs/Markdown/HelperFunctions.cs
@@ -0,0 +1,107 @@
+using Markdown.Tags;
+namespace Markdown;
+
+public class HelperFunctions
+{
+ private static readonly char[] forbiddenChars = ['_', '#'];
+
+ private static readonly char[] tagChars = ['_', '#', '\\'];
+
+ public static int FindCorrectCloseSymbolForItalic(string text, int startIndex)
+ {
+ for (int i = startIndex + 1; i < text.Length; i++)
+ {
+ if (text[i] == '_' && !IsPartOfDoubleUnderscore(text, i) && IsValidCloseSymbol(text, i))
+ return i;
+ }
+ return -1;
+ }
+
+ private static bool IsValidCloseSymbol(string text, int index)
+ {
+ if (index + 1 < text.Length && char.IsDigit(text[index + 1]))
+ return false;
+ if (index - 1 >= 0 && char.IsDigit(text[index - 1]))
+ {
+ if (index + 1 == text.Length || text.Substring(index + 1).All(char.IsWhiteSpace))
+ return true;
+
+ return false;
+ }
+ return true;
+ }
+
+ public static string ProcessNestedTag(ref string text)
+ {
+ List tagHandlers = new List
+ {
+ new BoldTag(),
+ new ItalicTag(),
+ new EscapeTag(),
+ new DefaultTagHandler()
+ };
+ Md md = new Md(tagHandlers);
+ return md.Render(text);
+ }
+
+ public static bool ContainsOnlyDash(string text) =>
+ text.All(symbol => forbiddenChars.Contains(symbol));
+
+ public static bool ContainsOnlyHeading(string text) =>
+ text.All(symbol => symbol == '#');
+
+ public static bool ContainsOnlySpases(string text) =>
+ string.IsNullOrWhiteSpace(text) || string.IsNullOrEmpty(text);
+
+ private static bool IsPartOfDoubleUnderscore(string text, int index) =>
+ (index + 1 < text.Length && text[index + 1] == '_') ||
+ (index > 0 && text[index - 1] == '_');
+
+ public static bool ContainsUnderscore(string text) => text.Contains('_');
+
+ public static bool ContainsSquareBrackets(string text) => text.Contains('[');
+
+ public static (List, List) GetUnderscoreIndexes(string text)
+ {
+ List singleUnderscoreIndexes = new List();
+ List doubleUnderscoreIndexes = new List();
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (text[i] == '_' && ((i + 1 < text.Length && text[i + 1] != '_') || i == text.Length - 1))
+ {
+ singleUnderscoreIndexes.Add(i);
+ }
+ if (i + 1 < text.Length && text[i] == '_' && text[i + 1] == '_')
+ {
+ doubleUnderscoreIndexes.Add(i);
+ i++;
+ }
+ }
+ return (singleUnderscoreIndexes, doubleUnderscoreIndexes);
+ }
+
+ public static bool HasUnpairedTags(List indexes1, List indexes2)
+ {
+ if (indexes1.Count < 2 || indexes2.Count < 2 || indexes1.Count % 2 == 1 || indexes2.Count % 2 == 1)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public static bool AreSegmentsIntersecting(Tuple segment1, Tuple segment2)
+ {
+ return segment1.Item2 >= segment2.Item1 && segment1.Item1 <= segment2.Item2;
+ }
+
+ public static bool AreSegmentsNested(Tuple segment1, Tuple segment2)
+ {
+ return (segment1.Item1 >= segment2.Item1 && segment1.Item2 <= segment2.Item2) ||
+ (segment2.Item1 >= segment1.Item1 && segment2.Item2 <= segment1.Item2);
+ }
+
+ public static string RemoveExtraSpaces(string input)
+ {
+ return string.Join(" ", input.Split(' ', StringSplitOptions.RemoveEmptyEntries));
+ }
+}
diff --git a/cs/Markdown/Markdown.csproj b/cs/Markdown/Markdown.csproj
new file mode 100644
index 000000000..cb9d759b7
--- /dev/null
+++ b/cs/Markdown/Markdown.csproj
@@ -0,0 +1,11 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ false
+
+
\ No newline at end of file
diff --git a/cs/Markdown/Md.cs b/cs/Markdown/Md.cs
new file mode 100644
index 000000000..5200ca575
--- /dev/null
+++ b/cs/Markdown/Md.cs
@@ -0,0 +1,41 @@
+using Markdown.Tags;
+
+namespace Markdown;
+
+public class Md
+{
+ private readonly List tagHandlers;
+
+ public Md(List? customTagHandlers = null)
+ {
+ tagHandlers = customTagHandlers ?? new List
+ {
+ new BoldTag(),
+ new ItalicTag(),
+ new HeadingTag(),
+ new EscapeTag(),
+ new LinkTag(),
+ new DefaultTagHandler()
+ };
+ }
+
+ public string Render(string markdownString)
+ {
+ var index = 0;
+ while (index < markdownString.Length)
+ TryProcessTag(ref markdownString, ref index);
+
+ return markdownString;
+ }
+
+ private void TryProcessTag(ref string markdownString, ref int index)
+ {
+ foreach (var handler in tagHandlers)
+ {
+ if (handler.IsTagStart(markdownString, index)){
+ index = handler.ProcessTag(ref markdownString, index);
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/cs/Markdown/Tags/BaseTagHandler.cs b/cs/Markdown/Tags/BaseTagHandler.cs
new file mode 100644
index 000000000..597f28ec0
--- /dev/null
+++ b/cs/Markdown/Tags/BaseTagHandler.cs
@@ -0,0 +1,159 @@
+using System.Runtime.InteropServices;
+
+namespace Markdown.Tags;
+
+public abstract class BaseTagHandler : ITagHandler
+{
+ protected abstract string Symbol { get; }
+ protected abstract string HtmlTag { get; }
+
+ public abstract bool IsTagStart(string text, int index);
+
+ public virtual int ProcessTag(ref string text, int startIndex)
+ {
+ if (HelperFunctions.ContainsOnlyDash(text.Substring(startIndex)))
+ return text.Length;
+ if (HelperFunctions.ContainsUnderscore(text) && CheckTagIntersections(text))
+ return text.Length;
+
+ var endIndex = 0;
+ if (startIndex + Symbol.Length < text.Length && char.IsDigit(text[startIndex + Symbol.Length]))
+ endIndex = FindEndIndexForDigit(text, startIndex);
+ else
+ endIndex = FindEndIndex(text, startIndex);
+
+ if (endIndex == -1)
+ return startIndex + Symbol.Length;
+
+ var content = ExtractContent(text, startIndex, endIndex);
+ if (HelperFunctions.ContainsOnlySpases(content))
+ return StringOnlySpaces(ref text, endIndex, Symbol.Length);
+ if (!AreTagsCorrectlyPositioned(text, startIndex, endIndex, content))
+ return startIndex + content.Length;
+
+ content = ProcessNestedTag(ref content);
+ var replacement = WrapWithHtmlTag(content);
+ text = ReplaceText(text, startIndex, endIndex, replacement);
+
+ return startIndex + replacement.Length;
+ }
+
+ private int FindEndIndexForDigit(string text, int startIndex)
+ {
+ var currentIndex = startIndex + Symbol.Length;
+
+ while (currentIndex < text.Length)
+ {
+ currentIndex = text.IndexOf(Symbol, currentIndex);
+ if (currentIndex == -1)
+ return -1;
+
+ if (currentIndex + Symbol.Length < text.Length && char.IsDigit(text[currentIndex + Symbol.Length]))
+ {
+ currentIndex += Symbol.Length;
+ continue;
+ }
+
+ if (currentIndex + Symbol.Length == text.Length || char.IsWhiteSpace(text[currentIndex + Symbol.Length]))
+ return currentIndex;
+
+ currentIndex += Symbol.Length;
+ }
+
+ return -1;
+ }
+
+ protected virtual int FindEndIndex(string text, int startIndex)
+ {
+ var currentIndex = startIndex + Symbol.Length;
+
+ while (currentIndex < text.Length)
+ {
+ currentIndex = text.IndexOf(Symbol, currentIndex);
+ if (currentIndex == -1)
+ return -1;
+
+ if (currentIndex + Symbol.Length < text.Length && char.IsDigit(text[currentIndex + Symbol.Length]))
+ {
+ currentIndex += Symbol.Length;
+ continue;
+ }
+ if (currentIndex > 0 && char.IsDigit(text[currentIndex - 1]))
+ {
+ if (currentIndex + Symbol.Length == text.Length ||
+ text.Substring(currentIndex + Symbol.Length).All(char.IsWhiteSpace))
+
+ return currentIndex;
+
+ currentIndex += Symbol.Length;
+ continue;
+ }
+ return currentIndex;
+ }
+
+ return -1;
+ }
+
+ private bool CheckTagIntersections(string text)
+ {
+ (List singleUnderscoreIndexes, List doubleUnderscoreIndexes) = HelperFunctions.GetUnderscoreIndexes(text);
+
+ if (!HelperFunctions.HasUnpairedTags(singleUnderscoreIndexes, doubleUnderscoreIndexes))
+ return false;
+
+ for (int i = 0; i < singleUnderscoreIndexes.Count - 1; i++)
+ {
+ for (int j = 0; j < doubleUnderscoreIndexes.Count - 1; j++)
+ {
+ var segment1 = Tuple.Create(singleUnderscoreIndexes[i], singleUnderscoreIndexes[i + 1]);
+ var segment2 = Tuple.Create(doubleUnderscoreIndexes[j], doubleUnderscoreIndexes[j + 1]);
+ if (HelperFunctions.AreSegmentsIntersecting(segment1, segment2) &&
+ !HelperFunctions.AreSegmentsNested(segment1, segment2))
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private static bool AreTagsCorrectlyPositioned(string text, int startIndex, int endIndex, string content)
+ {
+ if (!content.Contains(' '))
+ return true;
+ if (startIndex - 1 >= 0 && (char.IsLetter(text[startIndex - 1]) || char.IsDigit(text[startIndex - 1])))
+ return false;
+ if (endIndex + 1 < text.Length && (char.IsLetter(text[endIndex + 1]) || char.IsDigit(text[endIndex + 1])))
+ return false;
+ if (char.IsWhiteSpace(content.First()) || char.IsWhiteSpace(content.Last()))
+ return false;
+ return true;
+ }
+
+ protected virtual string ProcessNestedTag(ref string text)
+ {
+ return HelperFunctions.ProcessNestedTag(ref text);
+ }
+
+ protected virtual string ExtractContent(string text, int startIndex, int endIndex)
+ {
+ return text.Substring(startIndex + Symbol.Length, endIndex - startIndex - Symbol.Length);
+ }
+
+ protected virtual string WrapWithHtmlTag(string content)
+ {
+ return $"<{HtmlTag}>{content}{HtmlTag}>";
+ }
+
+ protected virtual string ReplaceText(string text, int startIndex, int endIndex, string replacement)
+ {
+ return text.Substring(0, startIndex) + replacement + text.Substring(endIndex + Symbol.Length);
+ }
+
+ protected virtual int StringOnlySpaces(ref string text, int endIndex, int symbolLength)
+ {
+ text = text.Substring(endIndex + symbolLength);
+ return endIndex;
+ }
+}
\ No newline at end of file
diff --git a/cs/Markdown/Tags/BoldTag.cs b/cs/Markdown/Tags/BoldTag.cs
new file mode 100644
index 000000000..5e74ab727
--- /dev/null
+++ b/cs/Markdown/Tags/BoldTag.cs
@@ -0,0 +1,9 @@
+namespace Markdown.Tags;
+public class BoldTag : BaseTagHandler
+{
+ protected override string Symbol => "__";
+ protected override string HtmlTag => "strong";
+
+ public override bool IsTagStart(string text, int index) => index + 2 <= text.Length
+ && text.Substring(index, 2) == Symbol;
+}
\ No newline at end of file
diff --git a/cs/Markdown/Tags/DefaultTag.cs b/cs/Markdown/Tags/DefaultTag.cs
new file mode 100644
index 000000000..64ef96144
--- /dev/null
+++ b/cs/Markdown/Tags/DefaultTag.cs
@@ -0,0 +1,14 @@
+namespace Markdown.Tags;
+
+public class DefaultTagHandler : ITagHandler
+{
+ public bool IsTagStart(string text, int index)
+ {
+ return true;
+ }
+
+ public int ProcessTag(ref string text, int startIndex)
+ {
+ return startIndex + 1;
+ }
+}
diff --git a/cs/Markdown/Tags/EscapeTag.cs b/cs/Markdown/Tags/EscapeTag.cs
new file mode 100644
index 000000000..bc0497f8d
--- /dev/null
+++ b/cs/Markdown/Tags/EscapeTag.cs
@@ -0,0 +1,25 @@
+namespace Markdown.Tags;
+public class EscapeTag : BaseTagHandler
+{
+ protected override string Symbol => "\\";
+ protected override string HtmlTag => "\\";
+
+ private readonly char[] forbiddenChars = ['_', '\\'];
+
+ public override bool IsTagStart(string text, int index) => text[index].ToString() == Symbol;
+
+ private bool CheckTag(string text, int startIndex)
+ => (startIndex + 1 < text.Length && forbiddenChars.Contains(text[startIndex + 1]))
+ || (startIndex + 3 < text.Length && text.Substring(startIndex + 1, 2) == "# ");
+
+ public override int ProcessTag(ref string text, int startIndex)
+ {
+ if (CheckTag(text, startIndex))
+ {
+ text = text.Remove(startIndex, 1);
+ if (startIndex + 2 < text.Length && text.Substring(startIndex, 2) == "__")
+ return startIndex + 2;
+ }
+ return startIndex + 1;
+ }
+}
\ No newline at end of file
diff --git a/cs/Markdown/Tags/HeadingTag.cs b/cs/Markdown/Tags/HeadingTag.cs
new file mode 100644
index 000000000..dad6f8b84
--- /dev/null
+++ b/cs/Markdown/Tags/HeadingTag.cs
@@ -0,0 +1,65 @@
+namespace Markdown.Tags;
+public class HeadingTag : BaseTagHandler
+{
+ protected override string Symbol => "#";
+ protected override string HtmlTag => "h1";
+
+ public override bool IsTagStart(string text, int index)
+ {
+ return (text[index].ToString() == Symbol && index + 1 < text.Length &&
+ char.IsWhiteSpace(text[index + 1])) || HelperFunctions.ContainsOnlyHeading(text);
+ }
+
+ public override int ProcessTag(ref string text, int startIndex)
+ {
+ var endIndex = FindEndIndex(text, startIndex);
+ var content = ExtractContent(text, startIndex, endIndex);
+ content = HelperFunctions.RemoveExtraSpaces(content);
+ if (HelperFunctions.ContainsOnlySpases(content) || HelperFunctions.ContainsOnlyHeading(content))
+ return StringOnlySpaces(ref text, endIndex, 0);
+
+ content = ProcessNestedTag(ref content);
+ var replacement = WrapWithHtmlTag(content);
+ text = ReplaceText(text, startIndex, endIndex, replacement);
+
+ return startIndex + replacement.Length;
+ }
+
+ protected override string ProcessNestedTag(ref string text) =>
+ HelperFunctions.ProcessNestedTag(ref text);
+
+ protected override string ExtractContent(string text, int startIndex, int endIndex)
+ {
+ startIndex = FindStartIndex(text, startIndex + 1, endIndex);
+ if (startIndex == -1)
+ return "";
+ return text.Substring(startIndex, endIndex - startIndex);
+ }
+
+ protected override int FindEndIndex(string text, int startIndex)
+ {
+ var endIndex = text.IndexOf('\n', startIndex + 1);
+ return endIndex == -1 ? text.Length : endIndex;
+ }
+
+ private int FindStartIndex(string text, int startIndex, int endIndex)
+ {
+ if (string.IsNullOrWhiteSpace(text.Substring(startIndex)))
+ return -1;
+
+ for (int i = startIndex; i < endIndex; i++)
+ {
+ if (!char.IsWhiteSpace(text[i]))
+ return i;
+ }
+ return -1;
+ }
+
+ protected override string ReplaceText(string text, int startIndex, int endIndex, string replacement)
+ {
+ if (endIndex < text.Length && text[endIndex] == '\n')
+ return text.Substring(0, startIndex) + replacement + '\n' + text.Substring(endIndex + 1);
+
+ return text.Substring(0, startIndex) + replacement + text.Substring(endIndex);
+ }
+}
diff --git a/cs/Markdown/Tags/ITagHandler.cs b/cs/Markdown/Tags/ITagHandler.cs
new file mode 100644
index 000000000..0b15435c3
--- /dev/null
+++ b/cs/Markdown/Tags/ITagHandler.cs
@@ -0,0 +1,7 @@
+namespace Markdown.Tags;
+public interface ITagHandler
+{
+ bool IsTagStart(string text, int index);
+
+ int ProcessTag(ref string text, int startIndex);
+}
diff --git a/cs/Markdown/Tags/ItalicTag.cs b/cs/Markdown/Tags/ItalicTag.cs
new file mode 100644
index 000000000..112db9d02
--- /dev/null
+++ b/cs/Markdown/Tags/ItalicTag.cs
@@ -0,0 +1,13 @@
+namespace Markdown.Tags;
+public class ItalicTag : BaseTagHandler
+{
+ protected override string Symbol => "_";
+ protected override string HtmlTag => "em";
+
+ public override bool IsTagStart(string text, int index) => text[index].ToString() == Symbol;
+
+ protected override int FindEndIndex(string text, int startIndex) =>
+ HelperFunctions.FindCorrectCloseSymbolForItalic(text, startIndex + 1);
+
+ protected override string ProcessNestedTag(ref string text) => text;
+}
\ No newline at end of file
diff --git a/cs/Markdown/Tags/LinkTag.cs b/cs/Markdown/Tags/LinkTag.cs
new file mode 100644
index 000000000..a6ecfc254
--- /dev/null
+++ b/cs/Markdown/Tags/LinkTag.cs
@@ -0,0 +1,71 @@
+namespace Markdown.Tags;
+
+public class LinkTag : BaseTagHandler
+{
+ protected override string Symbol => "[";
+ protected override string HtmlTag => "a";
+ private string HtmlTagLink => " href=";
+ public override bool IsTagStart(string text, int index) =>
+ text[index].ToString() == Symbol;
+
+ public override int ProcessTag(ref string text, int startIndex)
+ {
+ var endIndexOfNameLink = FindEndIndex(text, startIndex);
+ if (endIndexOfNameLink == -1)
+ return startIndex + 1;
+ var nameLink = ExtractContent(text, startIndex, endIndexOfNameLink);
+ nameLink = ProcessNestedTag(ref nameLink);
+ var startIndexOfLink = FindStartIndexOfLink(text, endIndexOfNameLink);
+ if (startIndexOfLink == -1)
+ return startIndex + 1;
+ var endIndexOfLink = FindEndIndexOfLink(text, endIndexOfNameLink);
+ if (endIndexOfLink == -1)
+ return startIndex + 1;
+ var link = ExtractContent(text, startIndexOfLink, endIndexOfLink);
+ var replacement = WrapWithHtmlTag(nameLink, link);
+ if (HelperFunctions.ContainsOnlySpases(nameLink))
+ replacement = "";
+ text = ReplaceText(text, startIndex, endIndexOfLink, replacement);
+ return startIndex + replacement.Length;
+
+ }
+ protected override int FindEndIndex(string text, int startIndex)
+ {
+ int bracketDepth = 0;
+ for (int i = startIndex; i < text.Length; i++)
+ {
+ if (text[i] == '[')
+ bracketDepth++;
+ else if (text[i] == ']')
+ {
+ bracketDepth--;
+ if (bracketDepth == 0)
+ {
+ if (i + 1 < text.Length && text[i + 1] == '(')
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ private int FindStartIndexOfLink(string text, int endIndexOfNameLink)
+ {
+ if (endIndexOfNameLink + 1 <= text.Length && text[endIndexOfNameLink + 1] == '(')
+ {
+ return endIndexOfNameLink + 1;
+ }
+ return -1;
+ }
+
+ private int FindEndIndexOfLink(string text, int endIndexOfNameLink)
+ {
+ var currentIndex = text.IndexOf(')', endIndexOfNameLink);
+ return currentIndex;
+ }
+
+ protected string WrapWithHtmlTag(string contentName, string contentLink)
+ {
+ return $"<{HtmlTag}{HtmlTagLink}{contentLink}>{contentName}{HtmlTag}>";
+ }
+}
diff --git a/cs/MarkdownTests/BoldTests.cs b/cs/MarkdownTests/BoldTests.cs
new file mode 100644
index 000000000..3ecec90cf
--- /dev/null
+++ b/cs/MarkdownTests/BoldTests.cs
@@ -0,0 +1,76 @@
+using FluentAssertions;
+using Markdown;
+
+namespace MarkdownTests;
+
+public class BoldTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp()
+ {
+ md = new Md();
+ }
+ [Test]
+ public void Test_StandartBoldWord()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__abc__4__").Should().Be("abc__4");
+ md.Render("__abc__4").Should().Be("__abc__4");
+ md.Render("__aaaa__").Should().Be("aaaa");
+ }
+
+ [Test]
+ public void Test_StandartBoldWords()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__aaaa__bbbb__cc__").Should().Be("aaaabbbbcc");
+ md.Render("__WORD__ BOB __PON__").Should().Be("WORD BOB PON");
+ md.Render("__aa bb__ __cc aa__").Should().Be("aa bb cc aa");
+ md.Render("__aa bb__").Should().Be("aa bb");
+ md.Render("__aa__ __bb__" + '\n' + "__aa__ __bb__").Should().Be("aa bb" + '\n' + "aa bb");
+ }
+
+ [Test]
+ public void Test_BoldWithOtherTags()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# __aaa__").Should().Be("aaa
");
+ md.Render("__aaa_b_a__").Should().Be("aaaba");
+ md.Render("# __aaa __").Should().Be("__aaa __
");
+ md.Render("_a __bbb__").Should().Be("_a bbb");
+ md.Render("__a _bbb__").Should().Be("a _bbb");
+ md.Render("# __aaa__ __bb__").Should().Be("aaa bb
");
+ md.Render("__aaa _b_ _cc_ a__").Should().Be("aaa b cc a");
+ md.Render("__aaa _b_ _cc _ a__").Should().Be("aaa b _cc _ a");
+ }
+
+ [Test]
+ public void Test_BoldInPartOfWord()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__нач__але").Should().Be("начале");
+ md.Render("сер__еди__не").Should().Be("середине");
+ md.Render("кон__це.__").Should().Be("конце.");
+ md.Render("aa сер__еди__не").Should().Be("aa середине");
+ md.Render("кон__це.__ bb").Should().Be("конце. bb");
+ }
+
+ [Test]
+ public void Test_BoldSeveralWords()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("ра__зных словах__").Should().Be("ра__зных словах__");
+ md.Render("ра__зных сл__овах").Should().Be("ра__зных сл__овах");
+ }
+
+ [Test]
+ public void Test_BoldTextWithSpaces()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__ __").Should().Be("");
+ md.Render("__ подчерки__").Should().Be("__ подчерки__");
+ md.Render("__подчерки __").Should().Be("__подчерки __");
+ }
+}
\ No newline at end of file
diff --git a/cs/MarkdownTests/BorderlineCases.cs b/cs/MarkdownTests/BorderlineCases.cs
new file mode 100644
index 000000000..a168e99d8
--- /dev/null
+++ b/cs/MarkdownTests/BorderlineCases.cs
@@ -0,0 +1,64 @@
+using FluentAssertions;
+using Markdown;
+namespace MarkdownTests;
+
+public class BorderlineCasesTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp(){
+ md = new Md();
+ }
+
+ [Test]
+ public void Test_TextWithDigits()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__aaa12__3__a").Should().Be("__aaa12__3__a");
+ md.Render("__12__3").Should().Be("__12__3");
+ md.Render("_12_3").Should().Be("_12_3");
+ md.Render("_aaa12_3_a_").Should().Be("aaa12_3_a");
+ md.Render("__aaa12_3_a__").Should().Be("aaa12_3_a");
+ md.Render("_aaa12__3__a_").Should().Be("aaa12__3__a");
+ md.Render("__aaa12__3__a__").Should().Be("aaa12__3__a");
+ md.Render("_123_").Should().Be("123");
+ md.Render("__123__").Should().Be("123");
+ md.Render("_123_ _a_").Should().Be("123 a");
+ md.Render("__123__ __a__").Should().Be("123 a");
+ }
+
+ [Test]
+ public void Test_TagsWithoutText()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("____").Should().Be("____");
+ md.Render("___").Should().Be("___");
+ md.Render("__").Should().Be("__");
+ md.Render("_").Should().Be("_");
+ }
+ [Test]
+ public void Test_TextWithUnpairedTags()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("__Непарные_").Should().Be("__Непарные_");
+ md.Render("_Непарные__").Should().Be("_Непарные__");
+ md.Render("__s_ __s").Should().Be("__s_ __s");
+ md.Render("__s _s__").Should().Be("s _s");
+ md.Render("__s_ s__").Should().Be("s_ s");
+ }
+
+ [Test]
+ public void Test_IntersectionOfTags()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("_a __bbb__ a_").Should().Be("a __bbb__ a");
+ md.Render("__a _bbb_ a__").Should().Be("a bbb a");
+ md.Render("__s _s__ _d_").Should().Be("s _s d");
+ md.Render("__s _s__ _d_").Should().Be("s _s d");
+ md.Render("__a _bbb__ a_").Should().Be("__a _bbb__ a_");
+ md.Render("a__ b_ _c __bb").Should().Be("a__ b_ _c __bb");
+ md.Render("__пересечения _двойных__ и одинарных_").Should().Be("__пересечения _двойных__ и одинарных_");
+ md.Render("_пересечения __двойных_ и одинарных__").Should().Be("_пересечения __двойных_ и одинарных__");
+ }
+}
\ No newline at end of file
diff --git a/cs/MarkdownTests/HeadingTests.cs b/cs/MarkdownTests/HeadingTests.cs
new file mode 100644
index 000000000..fbadc2ec4
--- /dev/null
+++ b/cs/MarkdownTests/HeadingTests.cs
@@ -0,0 +1,102 @@
+using Markdown;
+using FluentAssertions;
+
+namespace MarkdownTests;
+
+public class HeadingTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp()
+ {
+ md = new Md();
+ }
+ [Test]
+ public void Test_StandartHeading()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# aa# aa# aa# aa# aa# aa").Should().Be("aa# aa# aa# aa# aa# aa
");
+ md.Render("# aaaa").Should().Be("aaaa
");
+ md.Render("#aaaa").Should().Be("#aaaa");
+ }
+ [Test]
+ public void Test_HeadingTagsWithoutText()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("#\n# Заголовок").Should().Be("\nЗаголовок
");
+ md.Render("#").Should().Be("");
+ md.Render("##").Should().Be("");
+ md.Render("###").Should().Be("");
+ }
+
+ [Test]
+ public void Test_HeadingWithOtherTags()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# __a__" + "\n" + "# _b_").Should().Be("a
" + '\n' + "b
");
+ md.Render("# Заголовок __с _разными_ символами__").Should().Be("Заголовок с разными символами
");
+ md.Render("# __bold _italic_ text__").Should().Be("bold italic text
");
+ md.Render("# __bold _italic_ text__\n").Should().Be("bold italic text
\n");
+ }
+
+ [Test]
+ public void Test_HeadingWithTrailingSpaces()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# Заголовок").Should().Be("Заголовок
");
+ md.Render("# Заголовок ").Should().Be("Заголовок
");
+ md.Render("# Заголовок ф").Should().Be("Заголовок ф
");
+ }
+
+ [Test]
+ public void Test_InvalidHeadingWithoutSpace()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("##Heading").Should().Be("##Heading");
+ md.Render("#Heading").Should().Be("#Heading");
+ }
+
+ [Test]
+ public void Test_HeadingWithEmptyLines()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# aaa" + '\n' + "# bbb").Should().Be("aaa
" + '\n' + "bbb
");
+ md.Render("# aaa bbb" + '\n' + "# bbb").Should().Be("aaa bbb
" + '\n' + "bbb
");
+ md.Render("# aaa" + '\n' + "bbb").Should().Be("aaa
" + '\n' + "bbb");
+ md.Render("# abb" + "\n" + "ba_").Should().Be("abb
" + "\n" + "ba_");
+ md.Render("# Заголовок\n\n# Второй").Should().Be("Заголовок
\n\nВторой
");
+ }
+
+ [Test]
+ public void Test_HeadingWithSpecialCharacters()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# @#$%^&*() Заголовок").Should().Be("@#$%^&*() Заголовок
");
+ md.Render("# Заголовок с символами !@#").Should().Be("Заголовок с символами !@#
");
+ }
+
+ [Test]
+ public void Test_HeadingWithOnlyWhitespace()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# ").Should().Be("");
+ md.Render("#\t\t").Should().Be("");
+ }
+
+ [Test]
+ public void Test_HeadingMixedWithOtherText()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("Текст перед # Заголовок\nЕще текст").Should().Be("Текст перед Заголовок
\nЕще текст");
+ md.Render("# Заголовок\nОбычный текст\n# Второй").Should().Be("Заголовок
\nОбычный текст\nВторой
");
+ }
+
+ [Test]
+ public void Test_NestedTagsInsideHeading()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("# _italic_ **bold** text").Should().Be("italic **bold** text
");
+ md.Render("# **bold** _italic_ text").Should().Be("**bold** italic text
");
+ }
+}
\ No newline at end of file
diff --git a/cs/MarkdownTests/ItalicTests.cs b/cs/MarkdownTests/ItalicTests.cs
new file mode 100644
index 000000000..c7f929df5
--- /dev/null
+++ b/cs/MarkdownTests/ItalicTests.cs
@@ -0,0 +1,67 @@
+using Markdown;
+using FluentAssertions;
+using System.Reflection.Metadata;
+using System.ComponentModel.DataAnnotations;
+namespace MarkdownTests;
+
+public class ItailcTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp(){
+ md = new Md();
+ }
+ [Test]
+ public void Test_StandartItalicText()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("_abc_4").Should().Be("_abc_4");
+ md.Render("_abc_4_").Should().Be("abc_4");
+ md.Render("_abc w_ a _b s_").Should().Be("abc w a b s");
+ md.Render("_aaaa_").Should().Be("aaaa");
+ md.Render("_aaaa_bbbb_cc_").Should().Be("aaaabbbbcc");
+ md.Render("_aaaa_ bbb").Should().Be("aaaa bbb");
+ md.Render("_aaaa_ _bbbb_ _cc_").Should().Be("aaaa bbbb cc");
+ md.Render("_aaa bbb_").Should().Be("aaa bbb");
+ md.Render("ab_c_de").Should().Be("abcde");
+ md.Render("ab_c d_a").Should().Be("ab_c d_a");
+ md.Render("\\ _a_").Should().Be("\\ a");
+ md.Render("_bbb" + "\n" + "bb_").Should().Be("bbb" + "\n" + "bb");
+ }
+
+ [Test]
+ public void Test_NestedInItalicTags()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("_some __text__ in_").Should().Be("some __text__ in");
+ md.Render("# _aaa_").Should().Be("aaa
");
+ md.Render("_aaa__b__a_").Should().Be("aaa__b__a");
+ md.Render("_aaa __b__ a_").Should().Be("aaa __b__ a");
+ }
+ [Test]
+ public void Test_ItalicPartOfWord()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("_нач_але").Should().Be("начале");
+ md.Render("сер_еди_не").Should().Be("середине");
+ md.Render("кон_це._").Should().Be("конце.");
+ }
+
+ [Test]
+ public void Test_ItalicSeveralWords()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("ра_зных сл_овах").Should().Be("ра_зных сл_овах");
+ md.Render("ра_зных словах_").Should().Be("ра_зных словах_");
+ }
+
+ [Test]
+ public void Test_ItalicTextWithSpaces()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("_ _").Should().Be("");
+ md.Render("_ подчерки_").Should().Be("_ подчерки_");
+ md.Render("_подчерки _").Should().Be("_подчерки _");
+ }
+}
\ No newline at end of file
diff --git a/cs/MarkdownTests/LinkTests.cs b/cs/MarkdownTests/LinkTests.cs
new file mode 100644
index 000000000..de68eaafe
--- /dev/null
+++ b/cs/MarkdownTests/LinkTests.cs
@@ -0,0 +1,70 @@
+using FluentAssertions;
+using Markdown;
+
+namespace MarkdownTests;
+
+public class LinkTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp()
+ {
+ md = new Md();
+ }
+ [Test]
+ public void Test_StandartLinks()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[Link](https://link.com)").Should().Be("Link");
+ md.Render("[Link](https://example.com/?query=test&value=1)").Should().Be("Link");
+ md.Render("[Тестовая ссылка](https://пример.рф)").Should().Be("Тестовая ссылка");
+ md.Render("[Click me](https://example.com/#anchor)").Should().Be("Click me");
+ }
+
+ [Test]
+ public void Test_LinksWithoutName()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[]()").Should().Be("");
+ md.Render("[](aaa)").Should().Be("");
+ md.Render("[ ](aaa)").Should().Be("");
+ md.Render("(This is not a link)").Should().Be("(This is not a link)");
+ }
+
+ [Test]
+ public void Test_LinksWithoutLink()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[asd]()").Should().Be("asd");
+ md.Render("[This is not a link]").Should().Be("[This is not a link]");
+ }
+
+ [Test]
+ public void Test_LinksWithNestedBrackets()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[Nes [ted [Link]](https://example.com)").Should().Be("[Nes ted [Link]");
+ md.Render("[Nested [Link]](https://example.com)").Should().Be("Nested [Link]");
+ md.Render("[Link with ()](https://example.com)").Should().Be("Link with ()");
+ md.Render("[Nested Link]](https://example.com)").Should().Be("[Nested Link]](https://example.com)");
+ }
+
+ [Test]
+ public void Test_InvalidLinkFormatting()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[UnclosedLink](https://example.com").Should().Be("[UnclosedLink](https://example.com");
+ md.Render("Link](https://example.com)").Should().Be("Link](https://example.com)");
+ md.Render("[Link]https://example.com").Should().Be("[Link]https://example.com");
+ }
+
+ [Test]
+ public void Test_LinksWithNestedFormatting()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("[__Bold Link__](https://example.com)").Should().Be("Bold Link");
+ md.Render("[_Italic Link_](https://example.com)").Should().Be("Italic Link");
+ md.Render("[__Bold__ and _Italic_](https://example.com)").Should().Be("Bold and Italic");
+ }
+}
\ No newline at end of file
diff --git a/cs/MarkdownTests/MarkdownTests.csproj b/cs/MarkdownTests/MarkdownTests.csproj
new file mode 100644
index 000000000..25c84e0f0
--- /dev/null
+++ b/cs/MarkdownTests/MarkdownTests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cs/MarkdownTests/ScreenTests.cs b/cs/MarkdownTests/ScreenTests.cs
new file mode 100644
index 000000000..6676e9dbc
--- /dev/null
+++ b/cs/MarkdownTests/ScreenTests.cs
@@ -0,0 +1,30 @@
+using Markdown;
+using FluentAssertions;
+namespace MarkdownTests;
+
+public class ScreenTests
+{
+ private Md md;
+
+ [SetUp]
+ public void SetUp(){
+ md = new Md();
+ }
+ [Test]
+ public void Test_ScreenTests()
+ {
+ using var scope = new FluentAssertions.Execution.AssertionScope();
+ md.Render("\\# aaa").Should().Be("# aaa", "Экранированный символ в начале. Экранирует заголовочный тэг");
+ md.Render("\\#aa\\").Should().Be("\\#aa\\", "Экранированный символ в конце и начале, но тэг неправильно написан. Нет экранизации");
+ md.Render("\\\\\\aaa").Should().Be("\\\\aaa", "Три экранированных символов и строка без тэгов. Экранируется 2 экранируемых символа");
+ md.Render("\\__aaa__").Should().Be("__aaa__", "Экранированный символ в начале. Экранирует жирный тэг. У второго жирного тэга нет начала, поэтому он так остается");
+ md.Render("\\__aaa\\_").Should().Be("__aaa_", "Первый экранирует жирный. Второй курсив");
+ md.Render("\\\\aaa").Should().Be("\\aaa", "Два экранированных символов и строка без тэгов. Экранируется экранируемый символ");
+ md.Render("\\aaa").Should().Be("\\aaa", "Один экранированный символ и строка без тэгов. Ему нечего экранировать");
+ md.Render("\\__aaa\\__").Should().Be("__aaa__", "Два экранированных символа. Экранируют жирные тэги");
+ md.Render("\\_aaa_").Should().Be("_aaa_", "Экранированный символ в начале. Экранирует курсивный тэг. У второго курсивного тэга нет начала, поэтому он так остается");
+ md.Render("\\_aaa_\\").Should().Be("_aaa_\\", "Первый экранирует курсив. Второму нечего");
+ md.Render("\\__aaa\\").Should().Be("__aaa\\", "Первый экранирует жирный. Второму нечего");
+ md.Render("\\\\_aa_").Should().Be("\\aa", "Экранированный символ в начале, экранирует экранируемый.");
+ }
+}
\ No newline at end of file
diff --git a/cs/clean-code.sln b/cs/clean-code.sln
index 2206d54db..9c5ca0a4c 100644
--- a/cs/clean-code.sln
+++ b/cs/clean-code.sln
@@ -9,6 +9,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlDigit", "ControlDigi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples", "Samples\Samples.csproj", "{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Markdown", "Markdown\Markdown.csproj", "{58D91A68-12CD-4CF1-B593-2F0A0F3B0F05}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkdownTests", "MarkdownTests\MarkdownTests.csproj", "{70BA5D76-422C-4B82-ADA9-CC244D68468F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -27,5 +31,17 @@ Global
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3EF41D7-50EF-4CE1-B30A-D1D81C93D7FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {58D91A68-12CD-4CF1-B593-2F0A0F3B0F05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {58D91A68-12CD-4CF1-B593-2F0A0F3B0F05}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {58D91A68-12CD-4CF1-B593-2F0A0F3B0F05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {58D91A68-12CD-4CF1-B593-2F0A0F3B0F05}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F73079E3-EC6D-4300-B54D-977A5FB3B5A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F73079E3-EC6D-4300-B54D-977A5FB3B5A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F73079E3-EC6D-4300-B54D-977A5FB3B5A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F73079E3-EC6D-4300-B54D-977A5FB3B5A8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {70BA5D76-422C-4B82-ADA9-CC244D68468F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {70BA5D76-422C-4B82-ADA9-CC244D68468F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {70BA5D76-422C-4B82-ADA9-CC244D68468F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {70BA5D76-422C-4B82-ADA9-CC244D68468F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal