diff --git a/SimpleSudokuSolver.Tests/Strategy/HiddenPairTests.cs b/SimpleSudokuSolver.Tests/Strategy/HiddenPairTests.cs
new file mode 100644
index 0000000..747003b
--- /dev/null
+++ b/SimpleSudokuSolver.Tests/Strategy/HiddenPairTests.cs
@@ -0,0 +1,80 @@
+using NUnit.Framework;
+using SimpleSudokuSolver.Model;
+using SimpleSudokuSolver.Strategy;
+
+namespace SimpleSudokuSolver.Tests.Strategy
+{
+ public class HiddenPairTests : BaseStrategyTest
+ {
+ private readonly ISudokuSolverStrategy _strategy = new HiddenPair();
+
+ [Test]
+ public void HiddenPairTest1()
+ {
+ var sudoku = new int[,]
+ {
+ // From: http://www.sudokuwiki.org/Hidden_Candidates
+ { 0,0,0,0,0,0,0,0,0 },
+ { 9,0,4,6,0,7,0,0,0 },
+ { 0,7,6,8,0,4,1,0,0 },
+ { 3,0,9,7,0,1,0,8,0 },
+ { 0,0,8,0,0,0,3,0,0 },
+ { 0,5,0,3,0,8,7,0,2 },
+ { 0,0,7,5,0,2,6,1,0 },
+ { 0,0,0,4,0,3,2,0,8 },
+ { 0,0,0,0,0,0,0,0,0 }
+ };
+
+ var sudokuPuzzle = new SudokuPuzzle(sudoku);
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 7].CanBe, 2);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 7].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 7].CanBe, 4);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 7].CanBe, 5);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 7].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 4);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 5);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 9);
+ }
+
+ [Test]
+ public void HiddenPairTest2()
+ {
+ var sudoku = new int[,]
+ {
+ // From: http://www.sudokuwiki.org/Hidden_Candidates
+ { 7,2,0,4,0,8,0,3,0 },
+ { 0,8,0,0,0,0,0,4,7 },
+ { 4,0,1,0,7,6,8,0,2 },
+ { 8,1,0,7,3,9,0,0,0 },
+ { 0,0,0,8,5,1,0,0,0 },
+ { 0,0,0,2,6,4,0,8,0 },
+ { 2,0,9,6,8,0,4,1,3 },
+ { 3,4,0,0,0,0,0,0,8 },
+ { 1,6,8,9,4,3,2,7,5 }
+ };
+
+ var sudokuPuzzle = new SudokuPuzzle(sudoku);
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 2].CanBe, 5);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 2].CanBe, 6);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 1].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 2].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 2].CanBe, 6);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 2].CanBe, 7);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 6].CanBe, 6);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[4, 6].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 6].CanBe, 1);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 6].CanBe, 5);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 6].CanBe, 9);
+ }
+ }
+}
diff --git a/SimpleSudokuSolver.Tests/Strategy/HiddenQuadTests.cs b/SimpleSudokuSolver.Tests/Strategy/HiddenQuadTests.cs
new file mode 100644
index 0000000..066adff
--- /dev/null
+++ b/SimpleSudokuSolver.Tests/Strategy/HiddenQuadTests.cs
@@ -0,0 +1,83 @@
+using NUnit.Framework;
+using SimpleSudokuSolver.Model;
+using SimpleSudokuSolver.Strategy;
+
+namespace SimpleSudokuSolver.Tests.Strategy
+{
+ public class HiddenQuadTests : BaseStrategyTest
+ {
+ private readonly ISudokuSolverStrategy _strategy = new HiddenQuad();
+
+ [Test]
+ public void HiddenQuadTest1()
+ {
+ var sudoku = new int[,]
+ {
+ // From: http://www.sudokuwiki.org/Hidden_Candidates
+ { 6,5,0,0,8,7,0,2,4 },
+ { 0,0,0,6,4,9,0,5,0 },
+ { 0,4,0,0,2,5,0,0,0 },
+ { 5,7,0,4,3,8,0,6,1 },
+ { 0,0,0,5,0,1,0,0,0 },
+ { 3,1,0,9,0,2,0,8,5 },
+ { 0,0,0,8,9,0,0,1,0 },
+ { 0,0,0,2,1,3,0,0,0 },
+ { 1,3,0,7,5,0,0,9,8 }
+ };
+
+ var sudokuPuzzle = new SudokuPuzzle(sudoku);
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ // manual adaptation of CanBe so HiddenQuad can be applied
+ sudokuPuzzle.Cells[2, 6].CanBe.Remove(9);
+ sudokuPuzzle.Cells[4, 6].CanBe.Remove(2);
+ sudokuPuzzle.Cells[4, 6].CanBe.Remove(9);
+ sudokuPuzzle.Cells[6, 6].CanBe.Remove(2);
+ sudokuPuzzle.Cells[6, 6].CanBe.Remove(4);
+ sudokuPuzzle.Cells[8, 6].CanBe.Remove(4);
+
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[6, 6].CanBe, 6);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[7, 6].CanBe, 6);
+ }
+
+ [Test]
+ public void HiddenQuadTest2()
+ {
+ var sudoku = new int[,]
+ {
+ // From: http://www.sudokuwiki.org/Hidden_Candidates
+ { 9,0,1,5,0,0,0,4,6 },
+ { 4,2,5,0,9,0,0,8,1 },
+ { 8,6,0,0,1,0,0,2,0 },
+ { 5,0,2,0,0,0,0,0,0 },
+ { 0,1,9,0,0,0,4,6,0 },
+ { 6,0,0,0,0,0,0,0,2 },
+ { 1,9,6,0,4,0,2,5,3 },
+ { 2,0,0,0,6,0,8,1,7 },
+ { 0,0,0,0,0,1,6,9,4 }
+ };
+
+ var sudokuPuzzle = new SudokuPuzzle(sudoku);
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 3].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 3].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 3].CanBe, 8);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 5].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 5].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[3, 5].CanBe, 8);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 3].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 3].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 3].CanBe, 8);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 5].CanBe, 3);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 5].CanBe, 5);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 5].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 5].CanBe, 8);
+ }
+ }
+}
diff --git a/SimpleSudokuSolver.Tests/Strategy/HiddenTripleTests.cs b/SimpleSudokuSolver.Tests/Strategy/HiddenTripleTests.cs
new file mode 100644
index 0000000..08cc0d9
--- /dev/null
+++ b/SimpleSudokuSolver.Tests/Strategy/HiddenTripleTests.cs
@@ -0,0 +1,52 @@
+using NUnit.Framework;
+using SimpleSudokuSolver.Model;
+using SimpleSudokuSolver.Strategy;
+
+namespace SimpleSudokuSolver.Tests.Strategy
+{
+ public class HiddenTripleTests : BaseStrategyTest
+ {
+ private readonly ISudokuSolverStrategy _strategy = new HiddenTriple();
+
+ [Test]
+ public void HiddenTripleTest1()
+ {
+ var sudoku = new int[,]
+ {
+ // From: http://www.sudokuwiki.org/Hidden_Candidates
+ { 0,0,0,0,0,1,0,3,0 },
+ { 2,3,1,0,9,0,0,0,0 },
+ { 0,6,5,0,0,3,1,0,0 },
+ { 6,7,8,9,2,4,3,0,0 },
+ { 1,0,3,0,5,0,0,0,6 },
+ { 0,0,0,1,3,6,7,0,0 },
+ { 0,0,9,3,6,0,5,7,0 },
+ { 0,0,6,0,1,9,8,4,3 },
+ { 3,0,0,0,0,0,0,0,0 }
+ };
+
+ var sudokuPuzzle = new SudokuPuzzle(sudoku);
+ SolveUsingStrategy(sudokuPuzzle, _strategy);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 3].CanBe, 4);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 3].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 3].CanBe, 8);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 6].CanBe, 4);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 6].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 4);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 7);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 8);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[0, 8].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[1, 8].CanBe, 5);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[2, 8].CanBe, 2);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[2, 8].CanBe, 9);
+
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 8].CanBe, 2);
+ CollectionAssert.DoesNotContain(sudokuPuzzle.Cells[5, 8].CanBe, 9);
+ }
+ }
+}
diff --git a/SimpleSudokuSolver/DefaultSolver.cs b/SimpleSudokuSolver/DefaultSolver.cs
index e66c285..c3579a4 100644
--- a/SimpleSudokuSolver/DefaultSolver.cs
+++ b/SimpleSudokuSolver/DefaultSolver.cs
@@ -36,7 +36,10 @@ public DefaultSolver(params ISudokuSolverStrategy[] strategies)
new LockedCandidates(),
new NakedPair(),
new NakedTriple(),
- new NakedQuad()
+ new NakedQuad(),
+ new HiddenPair(),
+ new HiddenTriple(),
+ new HiddenQuad()
};
}
}
diff --git a/SimpleSudokuSolver/Strategy/HiddenPair.cs b/SimpleSudokuSolver/Strategy/HiddenPair.cs
new file mode 100644
index 0000000..76485b0
--- /dev/null
+++ b/SimpleSudokuSolver/Strategy/HiddenPair.cs
@@ -0,0 +1,50 @@
+using SimpleSudokuSolver.Model;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimpleSudokuSolver.Strategy
+{
+ ///
+ /// Strategy looks for two cells in the same row / column / block that have two candidate values that cannot
+ /// be in any other cell of the same row / column / block.
+ /// If such two cells are found, all other candidate values from those two cells can be removed.
+ ///
+ ///
+ /// See also:
+ /// - https://sudoku9x9.com/hidden_pair.html
+ /// - http://www.sudokuwiki.org/Hidden_Candidates
+ ///
+ public class HiddenPair : HiddenPairTripleQuadBase, ISudokuSolverStrategy
+ {
+ public string StrategyName => "Hidden Pair";
+
+ public SingleStepSolution SolveSingleStep(SudokuPuzzle sudokuPuzzle)
+ {
+ return GetSingleStepSolution(sudokuPuzzle, StrategyName);
+ }
+
+ protected override IEnumerable GetHiddenEliminations(
+ IEnumerable cells, SudokuPuzzle sudokuPuzzle)
+ {
+ var cellsWithNoValue = cells.Where(x => !x.HasValue).ToArray();
+ var hiddenCandidates = GetHiddenCandidates(cellsWithNoValue, sudokuPuzzle, 2);
+ var eliminations = new List();
+
+ for (int i = 1; i <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 1; i++)
+ {
+ for (int j = i + 1; j <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle; j++)
+ {
+ if (hiddenCandidates.ContainsKey(i) &&
+ hiddenCandidates.ContainsKey(j) &&
+ hiddenCandidates[i].SequenceEqual(hiddenCandidates[j]))
+ {
+ eliminations.AddRange(GetEliminations(hiddenCandidates[i][0], sudokuPuzzle, i, j));
+ eliminations.AddRange(GetEliminations(hiddenCandidates[i][1], sudokuPuzzle, i, j));
+ }
+ }
+ }
+
+ return eliminations;
+ }
+ }
+}
diff --git a/SimpleSudokuSolver/Strategy/HiddenPairTripleQuadBase.cs b/SimpleSudokuSolver/Strategy/HiddenPairTripleQuadBase.cs
new file mode 100644
index 0000000..4f70ef3
--- /dev/null
+++ b/SimpleSudokuSolver/Strategy/HiddenPairTripleQuadBase.cs
@@ -0,0 +1,83 @@
+using System.Collections.Generic;
+using System.Linq;
+using SimpleSudokuSolver.Model;
+
+namespace SimpleSudokuSolver.Strategy
+{
+ public abstract class HiddenPairTripleQuadBase
+ {
+ protected abstract IEnumerable GetHiddenEliminations(
+ IEnumerable cells, SudokuPuzzle sudokuPuzzle);
+
+ protected SingleStepSolution GetSingleStepSolution(SudokuPuzzle sudokuPuzzle, string strategyName)
+ {
+ var eliminations = new List();
+
+ foreach (var row in sudokuPuzzle.Rows)
+ {
+ eliminations.AddRange(GetHiddenEliminations(row.Cells, sudokuPuzzle));
+ }
+
+ foreach (var column in sudokuPuzzle.Columns)
+ {
+ eliminations.AddRange(GetHiddenEliminations(column.Cells, sudokuPuzzle));
+ }
+
+ foreach (var block in sudokuPuzzle.Blocks)
+ {
+ eliminations.AddRange(GetHiddenEliminations(block.Cells.OfType(), sudokuPuzzle));
+ }
+
+ return eliminations.Count > 0 ?
+ new SingleStepSolution(eliminations.Distinct().ToArray(), strategyName) :
+ null;
+ }
+
+ ///
+ /// Returns a dictionary where key is one of
+ /// and value is the collection of cells containing that value, but only if the
+ /// contains a certain number of such cells ().
+ ///
+ /// Empty cells of a single row/column/block.
+ /// Sudoku puzzle.
+ /// Tells how many cells containing a value we are looking for.
+ /// See summary.
+ protected IDictionary GetHiddenCandidates(Cell[] cellsWithNoValue, SudokuPuzzle sudokuPuzzle,
+ params int[] numberOfCellsContainingValue)
+ {
+ var candidates = new Dictionary();
+
+ foreach (var cellValue in sudokuPuzzle.PossibleCellValues)
+ {
+ var valueInCells = cellsWithNoValue.Where(x => x.CanBe.Contains(cellValue)).ToArray();
+ if (numberOfCellsContainingValue.Contains(valueInCells.Length))
+ candidates.Add(cellValue, valueInCells);
+ }
+
+ return candidates;
+ }
+
+ ///
+ /// For each member of's :
+ /// - if member is part of , ignore it
+ /// - if member is not part of return it as an elimination
+ ///
+ /// Cell which is analyzed for eliminations.
+ /// Sudoku puzzle to which the belongs.
+ /// Values which are not elimination candidates.
+ /// See summary.
+ protected IEnumerable GetEliminations(
+ Cell cell, SudokuPuzzle sudokuPuzzle, params int[] valuesToExclude)
+ {
+ var eliminations = new List();
+ var cellIndex = sudokuPuzzle.GetCellIndex(cell);
+ var eliminatedValues = cell.CanBe.Except(valuesToExclude);
+ foreach (var eliminatedValue in eliminatedValues)
+ {
+ eliminations.Add(new SingleStepSolution.Candidate(cellIndex.RowIndex, cellIndex.ColumnIndex, eliminatedValue));
+ }
+
+ return eliminations;
+ }
+ }
+}
diff --git a/SimpleSudokuSolver/Strategy/HiddenQuad.cs b/SimpleSudokuSolver/Strategy/HiddenQuad.cs
new file mode 100644
index 0000000..f6dd64a
--- /dev/null
+++ b/SimpleSudokuSolver/Strategy/HiddenQuad.cs
@@ -0,0 +1,64 @@
+using SimpleSudokuSolver.Model;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimpleSudokuSolver.Strategy
+{
+ ///
+ /// Strategy looks for four cells in the same row / column / block that have IN TOTAL four candidate values
+ /// that cannot be in any other cell of the same row / column / block.
+ /// If such four cells are found, all other candidate values from those four cells can be removed.
+ ///
+ ///
+ /// See also:
+ /// - https://sudoku9x9.com/hidden_pair.html
+ /// - http://www.sudokuwiki.org/Hidden_Candidates
+ ///
+ public class HiddenQuad : HiddenPairTripleQuadBase, ISudokuSolverStrategy
+ {
+ public string StrategyName => "Hidden Quad";
+
+ public SingleStepSolution SolveSingleStep(SudokuPuzzle sudokuPuzzle)
+ {
+ return GetSingleStepSolution(sudokuPuzzle, StrategyName);
+ }
+
+ protected override IEnumerable GetHiddenEliminations(
+ IEnumerable cells, SudokuPuzzle sudokuPuzzle)
+ {
+ var cellsWithNoValue = cells.Where(x => !x.HasValue).ToArray();
+ var hiddenCandidates = GetHiddenCandidates(cellsWithNoValue, sudokuPuzzle, 2, 3, 4);
+ var eliminations = new List();
+
+ for (int i = 1; i <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 3; i++)
+ {
+ for (int j = i + 1; j <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 2; j++)
+ {
+ for (int k = j + 1; k <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 1; k++)
+ {
+ for (int m = k + 1; m <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle; m++)
+ {
+ if (hiddenCandidates.ContainsKey(i) &&
+ hiddenCandidates.ContainsKey(j) &&
+ hiddenCandidates.ContainsKey(k) &&
+ hiddenCandidates.ContainsKey(m))
+ {
+ var union = hiddenCandidates[i].Union(hiddenCandidates[j]).
+ Union(hiddenCandidates[k]).Union(hiddenCandidates[m]).Distinct().ToArray();
+ if (union.Length == 4)
+ {
+ foreach (var cell in union)
+ {
+ eliminations.AddRange(GetEliminations(cell, sudokuPuzzle, i, j, k, m));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return eliminations;
+ }
+ }
+}
diff --git a/SimpleSudokuSolver/Strategy/HiddenTriple.cs b/SimpleSudokuSolver/Strategy/HiddenTriple.cs
new file mode 100644
index 0000000..b8c6fb8
--- /dev/null
+++ b/SimpleSudokuSolver/Strategy/HiddenTriple.cs
@@ -0,0 +1,60 @@
+using SimpleSudokuSolver.Model;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace SimpleSudokuSolver.Strategy
+{
+ ///
+ /// Strategy looks for three cells in the same row / column / block that have IN TOTAL three candidate values
+ /// that cannot be in any other cell of the same row / column / block.
+ /// If such three cells are found, all other candidate values from those three cells can be removed.
+ ///
+ ///
+ /// See also:
+ /// - https://sudoku9x9.com/hidden_pair.html
+ /// - http://www.sudokuwiki.org/Hidden_Candidates
+ ///
+ public class HiddenTriple : HiddenPairTripleQuadBase, ISudokuSolverStrategy
+ {
+ public string StrategyName => "Hidden Triple";
+
+ public SingleStepSolution SolveSingleStep(SudokuPuzzle sudokuPuzzle)
+ {
+ return GetSingleStepSolution(sudokuPuzzle, StrategyName);
+ }
+
+ protected override IEnumerable GetHiddenEliminations(
+ IEnumerable cells, SudokuPuzzle sudokuPuzzle)
+ {
+ var cellsWithNoValue = cells.Where(x => !x.HasValue).ToArray();
+ var hiddenCandidates = GetHiddenCandidates(cellsWithNoValue, sudokuPuzzle, 2, 3);
+ var eliminations = new List();
+
+ for (int i = 1; i <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 2; i++)
+ {
+ for (int j = i + 1; j <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle - 1; j++)
+ {
+ for (int k = j + 1; k <= sudokuPuzzle.NumberOfRowsOrColumnsInPuzzle; k++)
+ {
+ if (hiddenCandidates.ContainsKey(i) &&
+ hiddenCandidates.ContainsKey(j) &&
+ hiddenCandidates.ContainsKey(k))
+ {
+ var union = hiddenCandidates[i].Union(hiddenCandidates[j]).
+ Union(hiddenCandidates[k]).Distinct().ToArray();
+ if (union.Length == 3)
+ {
+ foreach (var cell in union)
+ {
+ eliminations.AddRange(GetEliminations(cell, sudokuPuzzle, i, j, k));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return eliminations;
+ }
+ }
+}
| | | | |