### [TDD]Find the Next Bigger Number from Codewars

Codewars 上的 kata: Next bigger number with the same digits

You have to create a function that takes a positive integer number and returns the next bigger number formed by the same digits:
f(12) == 21
f(513) == 531
f(2017) == 2071

If no bigger number can be composed using those digits, return -1:
f(9) == -1
f(111) == -1
f(531) == -1

    [TestClass]
public class UnitTest1
{
private const int NoLargerNumber = -1;

[TestMethod]
public void Test_1_should_be_largestNumber()
{
var input = 1;

var expected = NoLargerNumber;

int actual = NextLargerNumber.Next(input);

Assert.AreEqual(expected, actual);
}
}

    public static class NextLargerNumber
{
public static int Next(int input)
{
throw new NotImplementedException();
}
}

        public static int Next(int input)
{
return -1;
}

        [TestMethod]
public void Test_input_is_12_should_return_21()
{
var input = 12;
var expected = 21;

var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

1. 先將 input 轉成 char array, 再轉成 List<int> 以便後續處理。
2. 右邊的數字與左邊的數字 swap ，將 swap 完的 List<int> 透過 Select() + Math.Pow() 轉換成十進位數字。
3. 原本 return -1; 的邏輯，在這先定義為如果 input 產生的 List<int> 的 Count 只有 1 時，則 return -1; （等到別的測試案例因此失敗或重構時，再來調整。）

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

if (inputNumbers.Count == 1)
{
return -1;
}

var temp = inputNumbers[1];
inputNumbers[1] = inputNumbers[0];
inputNumbers[0] = temp;

int result = GetNumbericFromValueList(inputNumbers);
return result;
}

private static int GetNumbericFromValueList(List<int> inputNumbers)
{
var count = inputNumbers.Count;

var result = inputNumbers.Select((x, index) => x * (Math.Pow(10, (count - index - 1)))).Sum();
return Convert.ToInt32(result);
}

        [TestMethod]
public void Test_input_is_21_should_be_largestNumber()
{
var input = 21;
var expected = NoLargerNumber;
var actual = NextLargerNumber.Next(input);

Assert.AreEqual(expected, actual);
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

if (inputNumbers.Count == 1)
{
return -1;
}

if (inputNumbers[1] > inputNumbers[0])
{
var temp = inputNumbers[1];
inputNumbers[1] = inputNumbers[0];
inputNumbers[0] = temp;
}

int result = GetNumbericFromValueList(inputNumbers);
return result == input ? -1 : result;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

if (inputNumbers.Count == 1)
{
return -1;
}

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var temp = inputNumbers[index];
inputNumbers[index] = inputNumbers[index - 1];
inputNumbers[index - 1] = temp;
}
}

int result = GetNumbericFromValueList(inputNumbers);
return result == input ? -1 : result;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var temp = inputNumbers[index];
inputNumbers[index] = inputNumbers[index - 1];
inputNumbers[index - 1] = temp;
}
}

int result = GetNumbericFromValueList(inputNumbers);
return result == input ? -1 : result;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var temp = inputNumbers[index];
inputNumbers[index] = inputNumbers[index - 1];
inputNumbers[index - 1] = temp;

return GetNumbericFromValueList(inputNumbers);
}
}

return -1;
}

        [TestMethod]
public void Test_input_is_345_should_return_354()
{
var input = 345;
var expected = 354;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

        [TestMethod]
public void input_is_576_should_return_657()
{
var input = 576;
var expected = 657;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

1. 從 input 最右邊的位數往左邊一位比較，如果右邊比左邊小，則往下一位 shift 繼續比較。以這例子來說，就是 6 比 7 小，shift 下一位換 7 跟 5 比較。
2. 當右邊的數字，比左邊大時，我們先把原本的 input 以 L,T,R 來表示。以這例子來說，右邊的 7 比左邊的 5 大，要進行 swap 的處理。這時 T 就是 5，R 就是 {7, 6}，L 就是空集合
3. 右邊待 swap 的數字，從 R 的集合中，找出大於 T 的最小值。以這例子來說，就是從 {7,6} 找到比 5 大的最小值為 6
4. 將找到右邊待 swap 的數字，與 T 交換。
5. 針對新的  R 做升冪處理，才能確保是最小值的組合。
6. 最後的結果為 swap 完畢後的 L + T + R。

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
var rightFlag = inputNumbers[index];
var leftFlag = inputNumbers[index - 1];
if (rightFlag > leftFlag)
{
var t = leftFlag; //暫存 for swap
var r = inputNumbers.Skip(index).Take(inputNumbers.Count - index).ToList();
var l = inputNumbers.Take(index).ToList(); //包含t

for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > t)
{
l[index - 1] = r[i];
r[i] = t;
break; //找到第一個可以swap的，就是比t大的最小值
}
}

return GetNumbericFromValueList(l);
}
}

return -1;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var t = inputNumbers[index - 1]; //暫存 for swap
var r = inputNumbers.Skip(index).Take(inputNumbers.Count - index).ToList();
var l = inputNumbers.Take(index).ToList(); //包含t

for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > t)
{
l[index - 1] = r[i];
r[i] = t;
break; //找到第一個可以swap的，就是比t大的最小值
}
}

return GetNumbericFromValueList(l);
}
}

return -1;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var r = inputNumbers.Skip(index).Take(inputNumbers.Count - index).ToList();
var l = inputNumbers.Take(index).ToList();

for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > inputNumbers[index - 1])
{
l[index - 1] = r[i];
r[i] = inputNumbers[index - 1];
break;
}
}

return GetNumbericFromValueList(l);
}
}

return -1;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var r = inputNumbers.GetRange(index, inputNumbers.Count - index);
var l = inputNumbers.GetRange(0, index);

for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > inputNumbers[index - 1])
{
l[index - 1] = r[i];
r[i] = inputNumbers[index - 1];
break;
}
}

return GetNumbericFromValueList(l);
}
}

return -1;
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var r = inputNumbers.GetRange(index, inputNumbers.Count - index);
var l = inputNumbers.GetRange(0, index);

Swap(r, inputNumbers, index, l);

return GetNumbericFromValueList(l);
}
}

return -1;
}

private static void Swap(List<int> r, List<int> inputNumbers, int index, List<int> l)
{
for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > inputNumbers[index - 1])
{
l[index - 1] = r[i];
r[i] = inputNumbers[index - 1];
break;
}
}
}

        public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var r = inputNumbers.GetRange(index, inputNumbers.Count - index);
var l = inputNumbers.GetRange(0, index);

Swap(r, index, l);

return GetNumbericFromValueList(l);
}
}

return -1;
}

private static void Swap(List<int> r, int index, List<int> l)
{
var t = l[index - 1];
for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > t)
{
l[index - 1] = r[i];
r[i] = t;
break;
}
}
}

    public static class NextLargerNumber
{
public static int Next(int input)
{
var inputNumbers = input.ToString().ToCharArray().Select(x => (int)Char.GetNumericValue(x)).ToList();

for (int index = inputNumbers.Count - 1; index > 0; index--)
{
if (inputNumbers[index] > inputNumbers[index - 1])
{
var r = inputNumbers.GetRange(index, inputNumbers.Count - index);
var l = inputNumbers.GetRange(0, index);

Swap(r, index, l);

return GetNumbericFromValueList(l);
}
}

return -1;
}

private static void Swap(List<int> r, int index, List<int> l)
{
var t = l[index - 1];
for (int i = r.Count - 1; i >= 0; i--)
{
if (r[i] > t)
{
l[index - 1] = r[i];
r[i] = t;
break;
}
}
}

private static int GetNumbericFromValueList(List<int> inputNumbers)
{
var count = inputNumbers.Count;

var result = inputNumbers.Select((x, index) => x * (Math.Pow(10, (count - index - 1)))).Sum();
return Convert.ToInt32(result);
}
}

    [TestClass]
public class UnitTest1
{
private const int NoLargerNumber = -1;

[TestMethod]
public void Test_1_should_be_largestNumber()
{
var input = 1;
var expected = NoLargerNumber;
int actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_9_should_be_largestNumber()
{
var input = 9;
var expected = NoLargerNumber;
int actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_input_is_12_should_return_21()
{
var input = 12;
var expected = 21;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_input_is_21_should_be_largestNumber()
{
var input = 21;
var expected = NoLargerNumber;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_input_is_111_should_be_largestNumber()
{
var input = 111;
var expected = NoLargerNumber;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_input_is_531_should_be_largetNumber()
{
var input = 531;
var expected = NoLargerNumber;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_input_is_345_should_return_354()
{
var input = 345;
var expected = 354;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void input_is_576_should_return_657()
{
var input = 576;
var expected = 657;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_11200_should_return_12001()
{
var input = 11200;
var expected = 12001;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}

[TestMethod]
public void Test_15963_should_return_16359()
{
var input = 15963;
var expected = 16359;
var actual = NextLargerNumber.Next(input);
Assert.AreEqual(expected, actual);
}
}

## 結論

【摘要重點】

• 從最簡單的測試案例下手。
• 每次要新增的失敗的測試案例，都應該基於目前的測試案例集（或是指目前 production code 還少哪一個關鍵處理）去延伸設計一次只做一件最小但最重要的事，對工程師的人性來說相當具有挑戰。
• 重構一定要即時

【TDD 時必用的工具】

1. 地表最強的 IDE：Visual Studio。
2. Refactor 神兵：ReSharper。
3. 即時進行 TDD 神兵：Alive。（VS2017內建了）
4. Debug 測試案例執行過程神兵：OzCode。