From 2fbd25a7cdc6993dfb264717a15803311c968bdb Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 14:13:43 +0900 Subject: [PATCH 01/17] =?UTF-8?q?=E5=80=8B=E4=BA=BA=E7=9A=84=E3=81=ABx,y?= =?UTF-8?q?=E3=81=8C=E8=AA=AD=E3=81=BF=E3=81=A5=E3=82=89=E3=81=8B=E3=81=A3?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=81=A7rc=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 34 +++--- Tests/AtCoderSupportTests/Array2DTests.swift | 118 +++++++++---------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 195ccb7..bb76d7c 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -14,20 +14,20 @@ struct Array2D: Sequence, CustomStringConvertible { self.init(width: width, height: height, elements: [Element](repeating: element, count: width * height), outside: outside) } var count: Int { elements.count } - var xRange: Range { 0 ..< width } - var yRange: Range { 0 ..< height } - private func indexAt(x: Int, y: Int) -> Int? { - guard xRange.contains(x) else { return nil } - guard yRange.contains(y) else { return nil } - return y * width + x + var rowRange: Range { 0 ..< height } + var colRange: Range { 0 ..< width } + private func indexAt(r: Int, c: Int) -> Int? { + guard rowRange.contains(r) else { return nil } + guard colRange.contains(c) else { return nil } + return r * width + c } - subscript(x: Int, y: Int) -> Element { + subscript(r: Int, c: Int) -> Element { get { - guard let i = indexAt(x: x, y: y) else { return outside! } + guard let i = indexAt(r: r, c: c) else { return outside! } return elements[i] } set { - guard let i = indexAt(x: x, y: y) else { + guard let i = indexAt(r: r, c: c) else { precondition(outside != nil) return } @@ -46,12 +46,12 @@ struct Array2D: Sequence, CustomStringConvertible { } var description: String { var result: String = "" - for y in yRange { - for x in xRange { - if x > 0 { + for r in rowRange { + for c in colRange { + if c > 0 { result.append(" ") } - result.append("\(self[x, y])") + result.append("\(self[r, c])") } result.append("\n") } @@ -61,12 +61,12 @@ struct Array2D: Sequence, CustomStringConvertible { extension Array2D where Element: CustomStringConvertible { var description: String { var result: String = "" - for y in yRange { - for x in xRange { - if x > 0 { + for r in rowRange { + for c in colRange { + if c > 0 { result.append(" ") } - result.append(self[x, y].description) + result.append(self[r, c].description) } result.append("\n") } diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index aee0216..2f6f6da 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -7,12 +7,12 @@ final class Array2DTests: XCTestCase { let a: Array2D = .init(width: 3, height: 2, element: 42) XCTAssertEqual(a[0, 0], 42) - XCTAssertEqual(a[1, 0], 42) - XCTAssertEqual(a[2, 0], 42) - XCTAssertEqual(a[0, 1], 42) + XCTAssertEqual(a[0, 2], 42) + + XCTAssertEqual(a[1, 0], 42) XCTAssertEqual(a[1, 1], 42) - XCTAssertEqual(a[2, 1], 42) + XCTAssertEqual(a[1, 2], 42) } } @@ -33,19 +33,19 @@ final class Array2DTests: XCTestCase { ]) XCTAssertEqual(a[0, 0], 1) - XCTAssertEqual(a[1, 0], 2) - XCTAssertEqual(a[2, 0], 3) - XCTAssertEqual(a[3, 0], 4) + XCTAssertEqual(a[0, 1], 2) + XCTAssertEqual(a[0, 2], 3) + XCTAssertEqual(a[0, 3], 4) - XCTAssertEqual(a[0, 1], 5) + XCTAssertEqual(a[1, 0], 5) XCTAssertEqual(a[1, 1], 6) - XCTAssertEqual(a[2, 1], 7) - XCTAssertEqual(a[3, 1], 8) + XCTAssertEqual(a[1, 2], 7) + XCTAssertEqual(a[1, 3], 8) - XCTAssertEqual(a[0, 2], 9) - XCTAssertEqual(a[1, 2], 10) + XCTAssertEqual(a[2, 0], 9) + XCTAssertEqual(a[2, 1], 10) XCTAssertEqual(a[2, 2], 11) - XCTAssertEqual(a[3, 2], 12) + XCTAssertEqual(a[2, 3], 12) } do { let a: Array2D = .init(width: 4, height: 3, elements: [ @@ -55,29 +55,29 @@ final class Array2DTests: XCTestCase { ], outside: -1) XCTAssertEqual(a[0, 0], 1) - XCTAssertEqual(a[1, 0], 2) - XCTAssertEqual(a[2, 0], 3) - XCTAssertEqual(a[3, 0], 4) + XCTAssertEqual(a[0, 1], 2) + XCTAssertEqual(a[0, 2], 3) + XCTAssertEqual(a[0, 3], 4) - XCTAssertEqual(a[0, 1], 5) + XCTAssertEqual(a[1, 0], 5) XCTAssertEqual(a[1, 1], 6) - XCTAssertEqual(a[2, 1], 7) - XCTAssertEqual(a[3, 1], 8) + XCTAssertEqual(a[1, 2], 7) + XCTAssertEqual(a[1, 3], 8) - XCTAssertEqual(a[0, 2], 9) - XCTAssertEqual(a[1, 2], 10) + XCTAssertEqual(a[2, 0], 9) + XCTAssertEqual(a[2, 1], 10) XCTAssertEqual(a[2, 2], 11) - XCTAssertEqual(a[3, 2], 12) + XCTAssertEqual(a[2, 3], 12) XCTAssertEqual(a[-1, -1], -1) - XCTAssertEqual(a[4, -1], -1) - XCTAssertEqual(a[-1, 3], -1) - XCTAssertEqual(a[4, 3], -1) + XCTAssertEqual(a[-1, 4], -1) + XCTAssertEqual(a[3, -1], -1) + XCTAssertEqual(a[3, 4], -1) - XCTAssertEqual(a[-1, 0], -1) XCTAssertEqual(a[0, -1], -1) - XCTAssertEqual(a[4, 0], -1) - XCTAssertEqual(a[0, 3], -1) + XCTAssertEqual(a[-1, 0], -1) + XCTAssertEqual(a[0, 4], -1) + XCTAssertEqual(a[3, 0], -1) } } @@ -90,54 +90,54 @@ final class Array2DTests: XCTestCase { ], outside: -1) a[0, 0] = 100 - a[1, 0] = 200 - a[2, 0] = 300 - a[3, 0] = 400 + a[0, 1] = 200 + a[0, 2] = 300 + a[0, 3] = 400 - a[0, 1] = 500 + a[1, 0] = 500 a[1, 1] = 600 - a[2, 1] = 700 - a[3, 1] = 800 + a[1, 2] = 700 + a[1, 3] = 800 - a[0, 2] = 900 - a[1, 2] = 1000 + a[2, 0] = 900 + a[2, 1] = 1000 a[2, 2] = 1100 - a[3, 2] = 1200 + a[2, 3] = 1200 a[-1, -1] = -100 - a[4, -1] = -100 - a[-1, 3] = -100 - a[4, 3] = -100 + a[-1, 4] = -100 + a[3, -1] = -100 + a[3, 4] = -100 - a[-1, 0] = -100 - a[0, -1] = -100 - a[4, 0] = -100 - a[0, 3] = -100 + a[0, -1] = -123 + a[-1, 0] = -234 + a[0, 4] = -345 + a[3, 0] = -456 XCTAssertEqual(a[0, 0], 100) - XCTAssertEqual(a[1, 0], 200) - XCTAssertEqual(a[2, 0], 300) - XCTAssertEqual(a[3, 0], 400) + XCTAssertEqual(a[0, 1], 200) + XCTAssertEqual(a[0, 2], 300) + XCTAssertEqual(a[0, 3], 400) - XCTAssertEqual(a[0, 1], 500) + XCTAssertEqual(a[1, 0], 500) XCTAssertEqual(a[1, 1], 600) - XCTAssertEqual(a[2, 1], 700) - XCTAssertEqual(a[3, 1], 800) + XCTAssertEqual(a[1, 2], 700) + XCTAssertEqual(a[1, 3], 800) - XCTAssertEqual(a[0, 2], 900) - XCTAssertEqual(a[1, 2], 1000) + XCTAssertEqual(a[2, 0], 900) + XCTAssertEqual(a[2, 1], 1000) XCTAssertEqual(a[2, 2], 1100) - XCTAssertEqual(a[3, 2], 1200) + XCTAssertEqual(a[2, 3], 1200) XCTAssertEqual(a[-1, -1], -1) - XCTAssertEqual(a[4, -1], -1) - XCTAssertEqual(a[-1, 3], -1) - XCTAssertEqual(a[4, 3], -1) + XCTAssertEqual(a[-1, 4], -1) + XCTAssertEqual(a[3, -1], -1) + XCTAssertEqual(a[3, 4], -1) - XCTAssertEqual(a[-1, 0], -1) XCTAssertEqual(a[0, -1], -1) - XCTAssertEqual(a[4, 0], -1) - XCTAssertEqual(a[0, 3], -1) + XCTAssertEqual(a[-1, 0], -1) + XCTAssertEqual(a[0, 4], -1) + XCTAssertEqual(a[3, 0], -1) } } } From 7bade2b40399d8d11983f7602dd7d03494023ec2 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 17:20:18 +0900 Subject: [PATCH 02/17] =?UTF-8?q?force=20unwrap=20=E3=81=A7=E3=81=AF?= =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=82=A8=E3=83=A9=E3=83=BC=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=82=92=E5=87=BA=E3=81=99=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index bb76d7c..e107d25 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -23,7 +23,12 @@ struct Array2D: Sequence, CustomStringConvertible { } subscript(r: Int, c: Int) -> Element { get { - guard let i = indexAt(r: r, c: c) else { return outside! } + guard let i = indexAt(r: r, c: c) else { + guard let os = outside else { + fatalError("(r, c)=(\(r),\(c)) is outside and the outside value of Array2D is not defined.") + } + return os + } return elements[i] } set { From fdf62d64fc1aa76c926a1c4194884993ca2ae03e Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 17:21:09 +0900 Subject: [PATCH 03/17] =?UTF-8?q?=E5=BA=A7=E6=A8=99=E3=82=92typealias?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index e107d25..38c6058 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -1,4 +1,5 @@ struct Array2D: Sequence, CustomStringConvertible { + typealias Coord = (row: Int, col: Int) let width: Int let height: Int private(set) var elements: [Element] @@ -39,7 +40,7 @@ struct Array2D: Sequence, CustomStringConvertible { elements[i] = newValue } } - subscript(position: (Int, Int)) -> Element { + subscript(position: Coord) -> Element { get { self[position.0, position.1] } set { self[position.0, position.1] = newValue } } From 70b2e4afae7752339150630c42e8be6d5298e723 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 17:21:35 +0900 Subject: [PATCH 04/17] =?UTF-8?q?=E3=82=B7=E3=83=BC=E3=82=AF=E3=83=AF?= =?UTF-8?q?=E3=83=BC=E3=82=BA=E7=94=A8=E3=81=AE=E3=83=9C=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E3=82=8B=E3=81=A8=E3=81=8D=E3=81=AEinit?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 38c6058..1830ab4 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -79,3 +79,9 @@ extension Array2D where Element: CustomStringConvertible { return result } } +extension Array2D where Element == Character { + init(width: Int, height: Int, stringArray: [String], outside: Element? = nil) { + let array = stringArray.map { s in Array(s) }.flatMap { $0 } + self.init(width: width, height: height, elements: array, outside: outside) + } +} \ No newline at end of file From a0592c0c3583933d8c6be4e12731c67aee8a6a03 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 17:22:07 +0900 Subject: [PATCH 05/17] =?UTF-8?q?=E8=BF=91=E5=82=8D=E6=8E=A2=E7=B4=A2?= =?UTF-8?q?=E7=94=A8=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 32 ++++++++++ Tests/AtCoderSupportTests/Array2DTests.swift | 64 ++++++++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 1830ab4..5272859 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -50,6 +50,38 @@ struct Array2D: Sequence, CustomStringConvertible { func map(_ transform: (Element) throws -> T) rethrows -> Array2D { try Array2D(width: width, height: height, elements: elements.map(transform)) } + func neighboursCoord4(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { + let dr = [0, -1, 0, 1] + let dc = [1, 0, -1, 0] + var res = [Coord]() + for i in 0..<4 { + let nr = now.row + dr[i] + let nc = now.col + dc[i] + if ignoreOutside { + if indexAt(r: nr, c: nc) == nil { + continue + } + } + res.append((nr, nc)) + } + return res + } + func neighboursCoord8(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { + let dr = [0, -1, -1, -1, 0, 1, 1, 1] + let dc = [1, 1, 0, -1, -1, -1, 0, 1] + var res = [Coord]() + for i in 0..<8 { + let nr = now.row + dr[i] + let nc = now.col + dc[i] + if ignoreOutside { + if indexAt(r: nr, c: nc) == nil { + continue + } + } + res.append((nr, nc)) + } + return res + } var description: String { var result: String = "" for r in rowRange { diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index 2f6f6da..0016bc4 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -140,4 +140,68 @@ final class Array2DTests: XCTestCase { XCTAssertEqual(a[3, 0], -1) } } + + func testNeighbourCoord() { + let target = [ + "0*0", "0*1", "0*2", "0*3", + "1*0", "1*1", "1*2", "1*3", + "2*0", "2*1", "2*2", "2*3", + "3*0", "3*1", "3*2", "3*3", + "4*0", "4*1", "4*2", "4*3" + ] + let a: Array2D = .init(width: 4, height: 5, elements: target) + do { + let neighbourList = a.neighboursCoord4(around: (1, 1)) + let expectedList = [(1, 2), (0, 1), (1, 0), (2, 1)] + for (i, (r, c)) in neighbourList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let neighbourList = a.neighboursCoord4(around: (0, 1)) + let expectedList = [(0, 2), (0, 0), (1, 1)] + for (i, (r, c)) in neighbourList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let neighbourList = a.neighboursCoord4(around: (0, 0)) + let expectedList = [(0, 1), (1, 0)] + for (i, (r, c)) in neighbourList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let neighbourList = a.neighboursCoord4(around: (0, 0), ignoreOutside: false) + let expectedList = [(0, 1), (-1, 0), (0, -1), (1, 0)] + for (i, (r, c)) in neighbourList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + } + + func testSeekWords() { + let strs = [ + "asdfghjk", + "zxcvbnez", + "qwerykio", + "asdfuyui", + "qwenhjkl", + "qwscvbfu", + "nbvcytfr" + ] + let a: Array2D = .init(width: strs[0].count, height: strs.count, stringArray: strs) + + XCTAssertEqual(a[(5, 2)], "s") + XCTAssertEqual(a[(4, 3)], "n") + XCTAssertEqual(a[(3, 4)], "u") + XCTAssertEqual(a[(2, 5)], "k") + XCTAssertEqual(a[(1, 6)], "e") + + + } } From 03c4eba79fa9f205f25107cfe5ba2cff991fee20 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 17:22:19 +0900 Subject: [PATCH 06/17] =?UTF-8?q?modInt=E7=9A=84=E3=81=AA=E3=82=82?= =?UTF-8?q?=E3=81=AE=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Zn.swift | 51 ++++++++++++++++ Tests/AtCoderSupportTests/ZnTests.swift | 78 +++++++++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 Sources/AtCoderSupport/Zn.swift create mode 100644 Tests/AtCoderSupportTests/ZnTests.swift diff --git a/Sources/AtCoderSupport/Zn.swift b/Sources/AtCoderSupport/Zn.swift new file mode 100644 index 0000000..5bb14bd --- /dev/null +++ b/Sources/AtCoderSupport/Zn.swift @@ -0,0 +1,51 @@ +infix operator ** + +//fileprivate let mod = 1000000007 +fileprivate let mod = 998244353 + +struct Zn: CustomStringConvertible, AdditiveArithmetic, Hashable, ExpressibleByIntegerLiteral { + typealias IntegerLiteralType = Int + let n: Int + init(_ n: Int) { self.n = n } + init(safe n: Int) { let m = n % mod; if m >= 0 { self.n = m } else { self.n = m + mod } } + init(integerLiteral value: IntegerLiteralType) { n = value } + var description: String { String(n) } + static var zero = Zn(0) + static var one = Zn(1) + static prefix func - (value: Self) -> Self { Zn(value.n == 0 ? 0 : mod - value.n) } + static func + (lhs: Self, rhs: Self) -> Self { let n = lhs.n + rhs.n; return Zn(n < mod ? n : n - mod) } + static func + (lhs: Self, rhs: Int) -> Self { lhs + Zn(rhs) } + static func + (lhs: Int, rhs: Self) -> Self { Zn(lhs) + rhs } + static func - (lhs: Self, rhs: Self) -> Self { let n = lhs.n - rhs.n; return Zn(n >= 0 ? n : n + mod) } + static func - (lhs: Self, rhs: Int) -> Self { lhs - Zn(rhs) } + static func - (lhs: Int, rhs: Self) -> Self { Zn(lhs) - rhs } + static func * (lhs: Self, rhs: Self) -> Self { Zn((lhs.n * rhs.n) % mod) } + static func * (lhs: Self, rhs: Int) -> Self { lhs * Zn(rhs) } + static func * (lhs: Int, rhs: Self) -> Self { Zn(lhs) * rhs } + static func / (lhs: Self, rhs: Self) -> Self { lhs * rhs.inverse } + static func / (lhs: Int, rhs: Self) -> Self { Zn(lhs) * rhs.inverse } + static func / (lhs: Self, rhs: Int) -> Self { lhs * Zn(rhs).inverse } + static func += (lhs: inout Self, rhs: Self) { lhs = lhs + rhs } + static func += (lhs: inout Self, rhs: Int) { lhs = lhs + Zn(rhs) } + static func -= (lhs: inout Self, rhs: Self) { lhs = lhs - rhs } + static func -= (lhs: inout Self, rhs: Int) { lhs = lhs - Zn(rhs) } + static func *= (lhs: inout Self, rhs: Self) { lhs = lhs * rhs } + static func *= (lhs: inout Self, rhs: Int) { lhs = lhs * Zn(rhs) } + static func /= (lhs: inout Self, rhs: Self) { lhs = lhs / rhs } + static func /= (lhs: inout Self, rhs: Int) { lhs = lhs / Zn(rhs) } + static func ** (lhs: Self, rhs: Int) -> Self { lhs.pow(rhs) } + + func pow(_ n: Int) -> Self { + if n < 0 { return inverse.pow(-n) } + var result: Zn = 1 + var x: Zn = self + var n = n + while n > 0 { + if n & 1 == 1 { result *= x } + x *= x + n >>= 1 + } + return result + } + var inverse: Self { pow(mod - 2) } +} diff --git a/Tests/AtCoderSupportTests/ZnTests.swift b/Tests/AtCoderSupportTests/ZnTests.swift new file mode 100644 index 0000000..ebf5952 --- /dev/null +++ b/Tests/AtCoderSupportTests/ZnTests.swift @@ -0,0 +1,78 @@ +import XCTest +@testable import AtCoderSupport + +final class ZnTests: XCTestCase { + func testNegation() { + assert(-Zn(safe: 5) == Zn(safe: -5)) + assert(-Zn(safe: 0) == 0) + } + + func testAddition() { + assert((Zn(safe: 3) + Zn(safe: 5)).n == Zn(safe: 8).n) + assert((Zn(safe: 5) + Zn(safe: -4)).n == Zn(safe: 1).n) + } + + func testSubtraction() { + assert(Zn(safe: 5) - Zn(safe: 3) == Zn(safe: 2)) + assert(Zn(safe: 3) - Zn(safe: 5) == Zn(safe: -2)) + assert(Zn(safe: 3) - Zn(safe: 3) == Zn(safe: 0)) + } + + func testMultiplication() { + assert(Zn(safe: 3) * Zn(safe: 4) == Zn(safe: 12)) + assert(Zn(safe: 3) * Zn(safe: -4) == Zn(safe: -12)) + assert(Zn(safe: -3) * Zn(safe: 4) == Zn(safe: -12)) + assert(Zn(safe: -3) * Zn(safe: -4) == Zn(safe: 12)) + assert(Zn(safe: 3) * Zn(safe: 0) == Zn(safe: 0)) + assert(Zn(safe: 0) * Zn(safe: 3) == Zn(safe: 0)) + assert(Zn(safe: 0) * Zn(safe: 0) == Zn(safe: 0)) + } + + func testDivision() { + assert(Zn(safe: 3) / Zn(safe: 2) * Zn(safe: 2) == Zn(safe: 3)) + assert(Zn(safe: 3) / Zn(safe: -2) * Zn(safe: -2) == Zn(safe: 3)) + assert(Zn(safe: 3) / Zn(safe: 1) * Zn(safe: 1) == Zn(safe: 3)) + assert(Zn(safe: 3) / Zn(safe: -1) * Zn(safe: -1) == Zn(safe: 3)) + assert(Zn(safe: 0) / Zn(safe: 3) * Zn(safe: 3) == Zn(safe: 0)) + } + + func testPower() { + assert(Zn(safe: 2).pow(0) == Zn(safe: 1)) + assert(Zn(safe: 2).pow(1) == Zn(safe: 2)) + assert(Zn(safe: 2).pow(2) == Zn(safe: 2 * 2)) + assert(Zn(safe: 2).pow(3) == Zn(safe: 2 * 2 * 2)) + assert(Zn(safe: 2).pow(4) == Zn(safe: 2 * 2 * 2 * 2)) + assert(Zn(safe: 2).pow(5) == Zn(safe: 2 * 2 * 2 * 2 * 2)) + assert(Zn(safe: 2).pow(6) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2)) + assert(Zn(safe: 2).pow(7) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2 * 2)) + assert(Zn(safe: 2).pow(8) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2)) + assert(Zn(safe: 2).pow(-1) == Zn(safe: 2).inverse) + assert(Zn(safe: 2).pow(-2) == Zn(safe: 2 * 2).inverse) + assert(Zn(safe: 2).pow(-3) == Zn(safe: 2 * 2 * 2).inverse) + assert(Zn(safe: 2).pow(-4) == Zn(safe: 2 * 2 * 2 * 2).inverse) + assert(Zn(safe: 2).pow(-5) == Zn(safe: 2 * 2 * 2 * 2 * 2).inverse) + assert(Zn(safe: 2).pow(-6) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2).inverse) + assert(Zn(safe: 2).pow(-7) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2 * 2).inverse) + assert(Zn(safe: 2).pow(-8) == Zn(safe: 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2).inverse) + assert(Zn(safe: 3).pow(0) == Zn(safe: 1)) + assert(Zn(safe: 3).pow(1) == Zn(safe: 3)) + assert(Zn(safe: 3).pow(2) == Zn(safe: 3 * 3)) + assert(Zn(safe: 3).pow(3) == Zn(safe: 3 * 3 * 3)) + assert(Zn(safe: 3).pow(4) == Zn(safe: 3 * 3 * 3 * 3)) + assert(Zn(safe: 3).pow(5) == Zn(safe: 3 * 3 * 3 * 3 * 3)) + assert(Zn(safe: 3).pow(6) == Zn(safe: 3 * 3 * 3 * 3 * 3 * 3)) + assert(Zn(safe: 3).pow(7) == Zn(safe: 3 * 3 * 3 * 3 * 3 * 3 * 3)) + assert(Zn(safe: 3).pow(8) == Zn(safe: 3 * 3 * 3 * 3 * 3 * 3 * 3 * 3)) + } + + func testInverse() { + assert(Zn(safe: 1).inverse * Zn(safe: 1) == Zn(safe: 1)) + assert(Zn(safe: 2).inverse * Zn(safe: 2) == Zn(safe: 1)) + assert(Zn(safe: 3).inverse * Zn(safe: 3) == Zn(safe: 1)) + assert(Zn(safe: 4).inverse * Zn(safe: 4) == Zn(safe: 1)) + assert(Zn(safe: 5).inverse * Zn(safe: 5) == Zn(safe: 1)) + assert(Zn(safe: 6).inverse * Zn(safe: 6) == Zn(safe: 1)) + assert(Zn(safe: 7).inverse * Zn(safe: 7) == Zn(safe: 1)) + assert(Zn(safe: 8).inverse * Zn(safe: 8) == Zn(safe: 1)) + } +} \ No newline at end of file From e7d8314bd7d379935791b71c774d6edbb8971368 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 19:53:13 +0900 Subject: [PATCH 07/17] =?UTF-8?q?=E3=83=AA=E3=83=95=E3=82=A1=E3=82=AF?= =?UTF-8?q?=E3=82=BF=E3=83=AA=E3=83=B3=E3=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 5272859..46bc7c3 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -1,5 +1,8 @@ struct Array2D: Sequence, CustomStringConvertible { typealias Coord = (row: Int, col: Int) + let quadDirectionUnitVec = [(0, 1), (-1, 0), (0, -1), (1, 0)] + let octaDirectionUnitVec = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)] + let width: Int let height: Int private(set) var elements: [Element] @@ -17,6 +20,8 @@ struct Array2D: Sequence, CustomStringConvertible { var count: Int { elements.count } var rowRange: Range { 0 ..< height } var colRange: Range { 0 ..< width } + private func boardContains(coord: Coord) -> Bool { rowRange.contains(coord.row) && colRange.contains(coord.col) } + private func boardNotContains(coord: Coord) -> Bool { !boardContains(coord: coord) } private func indexAt(r: Int, c: Int) -> Int? { guard rowRange.contains(r) else { return nil } guard colRange.contains(c) else { return nil } @@ -51,28 +56,22 @@ struct Array2D: Sequence, CustomStringConvertible { try Array2D(width: width, height: height, elements: elements.map(transform)) } func neighboursCoord4(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { - let dr = [0, -1, 0, 1] - let dc = [1, 0, -1, 0] var res = [Coord]() for i in 0..<4 { - let nr = now.row + dr[i] - let nc = now.col + dc[i] - if ignoreOutside { - if indexAt(r: nr, c: nc) == nil { - continue - } + let nr = now.row + quadDirectionUnitVec[i].0 + let nc = now.col + quadDirectionUnitVec[i].1 + if ignoreOutside && boardNotContains(coord: (row: nr, col: nc)) { + continue } res.append((nr, nc)) } return res } func neighboursCoord8(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { - let dr = [0, -1, -1, -1, 0, 1, 1, 1] - let dc = [1, 1, 0, -1, -1, -1, 0, 1] var res = [Coord]() for i in 0..<8 { - let nr = now.row + dr[i] - let nc = now.col + dc[i] + let nr = now.row + octaDirectionUnitVec[i].0 + let nc = now.col + octaDirectionUnitVec[i].1 if ignoreOutside { if indexAt(r: nr, c: nc) == nil { continue From effb8f91dc9bca7db5b004090f480fb1af44d037 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 19:53:30 +0900 Subject: [PATCH 08/17] =?UTF-8?q?=E3=82=B7=E3=83=BC=E3=82=AF=E3=83=AF?= =?UTF-8?q?=E3=83=BC=E3=82=BA=E7=94=A8=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 21 ++++++++++++++++++++ Tests/AtCoderSupportTests/Array2DTests.swift | 15 +++++++------- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 46bc7c3..a688598 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -115,4 +115,25 @@ extension Array2D where Element == Character { let array = stringArray.map { s in Array(s) }.flatMap { $0 } self.init(width: width, height: height, elements: array, outside: outside) } + func seek(word: String) -> [Coord]? { + let wcs = Array(word) + for row in 0.. = .init(width: strs[0].count, height: strs.count, stringArray: strs) - - XCTAssertEqual(a[(5, 2)], "s") - XCTAssertEqual(a[(4, 3)], "n") - XCTAssertEqual(a[(3, 4)], "u") - XCTAssertEqual(a[(2, 5)], "k") - XCTAssertEqual(a[(1, 6)], "e") - - + let result = a.seek(word: "snuke") + XCTAssertNotNil(result) + let expected = [(5, 2), (4, 3), (3, 4), (2, 5), (1, 6)] + for (i, (r, c)) in result!.enumerated() { + XCTAssertEqual(r, expected[i].0) + XCTAssertEqual(c, expected[i].1) + } } } From cb263728d9ce1c510f9d35d7a3b47ffdf163af40 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 21 May 2023 20:00:55 +0900 Subject: [PATCH 09/17] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=AE=E9=A0=86?= =?UTF-8?q?=E7=95=AA=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 12 ++++++------ Tests/AtCoderSupportTests/Array2DTests.swift | 14 +++++++------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index a688598..f42b0e8 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -7,15 +7,15 @@ struct Array2D: Sequence, CustomStringConvertible { let height: Int private(set) var elements: [Element] let outside: Element? - init(width: Int, height: Int, elements: [Element], outside: Element? = nil) { + init(height: Int, width: Int, elements: [Element], outside: Element? = nil) { precondition(elements.count == width * height) self.width = width self.height = height self.elements = elements self.outside = outside } - init(width: Int, height: Int, element: Element, outside: Element? = nil) { - self.init(width: width, height: height, elements: [Element](repeating: element, count: width * height), outside: outside) + init(height: Int, width: Int, element: Element, outside: Element? = nil) { + self.init(height: height, width: width, elements: [Element](repeating: element, count: width * height), outside: outside) } var count: Int { elements.count } var rowRange: Range { 0 ..< height } @@ -53,7 +53,7 @@ struct Array2D: Sequence, CustomStringConvertible { elements.makeIterator() } func map(_ transform: (Element) throws -> T) rethrows -> Array2D { - try Array2D(width: width, height: height, elements: elements.map(transform)) + try Array2D(height: height, width: width, elements: elements.map(transform)) } func neighboursCoord4(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { var res = [Coord]() @@ -111,9 +111,9 @@ extension Array2D where Element: CustomStringConvertible { } } extension Array2D where Element == Character { - init(width: Int, height: Int, stringArray: [String], outside: Element? = nil) { + init(height: Int, width: Int, stringArray: [String], outside: Element? = nil) { let array = stringArray.map { s in Array(s) }.flatMap { $0 } - self.init(width: width, height: height, elements: array, outside: outside) + self.init(height: height, width: width, elements: array, outside: outside) } func seek(word: String) -> [Coord]? { let wcs = Array(word) diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index cbe2365..376dd3e 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -4,7 +4,7 @@ import XCTest final class Array2DTests: XCTestCase { func testInit() { do { - let a: Array2D = .init(width: 3, height: 2, element: 42) + let a: Array2D = .init(height: 2, width: 3, element: 42) XCTAssertEqual(a[0, 0], 42) XCTAssertEqual(a[0, 1], 42) @@ -18,7 +18,7 @@ final class Array2DTests: XCTestCase { func testCount() { do { - let a: Array2D = .init(width: 4, height: 3, element: 42) + let a: Array2D = .init(height: 3, width: 4, element: 42) let r = a.count XCTAssertEqual(r, 12) } @@ -26,7 +26,7 @@ final class Array2DTests: XCTestCase { func testSubscriptGet() { do { - let a: Array2D = .init(width: 4, height: 3, elements: [ + let a: Array2D = .init(height: 3, width: 4, elements: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, @@ -48,7 +48,7 @@ final class Array2DTests: XCTestCase { XCTAssertEqual(a[2, 3], 12) } do { - let a: Array2D = .init(width: 4, height: 3, elements: [ + let a: Array2D = .init(height: 3, width: 4, elements: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, @@ -83,7 +83,7 @@ final class Array2DTests: XCTestCase { func testSubscriptSet() { do { - var a: Array2D = .init(width: 4, height: 3, elements: [ + var a: Array2D = .init(height: 3, width: 4, elements: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, @@ -149,7 +149,7 @@ final class Array2DTests: XCTestCase { "3*0", "3*1", "3*2", "3*3", "4*0", "4*1", "4*2", "4*3" ] - let a: Array2D = .init(width: 4, height: 5, elements: target) + let a: Array2D = .init(height: 5, width: 4, elements: target) do { let neighbourList = a.neighboursCoord4(around: (1, 1)) let expectedList = [(1, 2), (0, 1), (1, 0), (2, 1)] @@ -194,7 +194,7 @@ final class Array2DTests: XCTestCase { "qwscvbfu", "nbvcytfr" ] - let a: Array2D = .init(width: strs[0].count, height: strs.count, stringArray: strs) + let a: Array2D = .init(height: strs.count, width: strs[0].count, stringArray: strs) let result = a.seek(word: "snuke") XCTAssertNotNil(result) let expected = [(5, 2), (4, 3), (3, 4), (2, 5), (1, 6)] From 0a16ffb33f793d71897acb18f13db125f0e764cc Mon Sep 17 00:00:00 2001 From: log5r Date: Sat, 27 May 2023 17:06:44 +0900 Subject: [PATCH 10/17] =?UTF-8?q?=E5=90=8D=E5=89=8D=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=EF=BC=8B=E3=83=86=E3=82=B9=E3=83=88=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 4 +- Tests/AtCoderSupportTests/Array2DTests.swift | 51 ++++++++++++++++++-- 2 files changed, 49 insertions(+), 6 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index f42b0e8..693a385 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -55,7 +55,7 @@ struct Array2D: Sequence, CustomStringConvertible { func map(_ transform: (Element) throws -> T) rethrows -> Array2D { try Array2D(height: height, width: width, elements: elements.map(transform)) } - func neighboursCoord4(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { + func neighbours(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { var res = [Coord]() for i in 0..<4 { let nr = now.row + quadDirectionUnitVec[i].0 @@ -67,7 +67,7 @@ struct Array2D: Sequence, CustomStringConvertible { } return res } - func neighboursCoord8(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { + func surroundings(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { var res = [Coord]() for i in 0..<8 { let nr = now.row + octaDirectionUnitVec[i].0 diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index 376dd3e..b2f5992 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -151,7 +151,7 @@ final class Array2DTests: XCTestCase { ] let a: Array2D = .init(height: 5, width: 4, elements: target) do { - let neighbourList = a.neighboursCoord4(around: (1, 1)) + let neighbourList = a.neighbours(around: (1, 1)) let expectedList = [(1, 2), (0, 1), (1, 0), (2, 1)] for (i, (r, c)) in neighbourList.enumerated() { XCTAssertEqual(r, expectedList[i].0) @@ -159,7 +159,7 @@ final class Array2DTests: XCTestCase { } } do { - let neighbourList = a.neighboursCoord4(around: (0, 1)) + let neighbourList = a.neighbours(around: (0, 1)) let expectedList = [(0, 2), (0, 0), (1, 1)] for (i, (r, c)) in neighbourList.enumerated() { XCTAssertEqual(r, expectedList[i].0) @@ -167,7 +167,7 @@ final class Array2DTests: XCTestCase { } } do { - let neighbourList = a.neighboursCoord4(around: (0, 0)) + let neighbourList = a.neighbours(around: (0, 0)) let expectedList = [(0, 1), (1, 0)] for (i, (r, c)) in neighbourList.enumerated() { XCTAssertEqual(r, expectedList[i].0) @@ -175,7 +175,7 @@ final class Array2DTests: XCTestCase { } } do { - let neighbourList = a.neighboursCoord4(around: (0, 0), ignoreOutside: false) + let neighbourList = a.neighbours(around: (0, 0), ignoreOutside: false) let expectedList = [(0, 1), (-1, 0), (0, -1), (1, 0)] for (i, (r, c)) in neighbourList.enumerated() { XCTAssertEqual(r, expectedList[i].0) @@ -184,6 +184,49 @@ final class Array2DTests: XCTestCase { } } + func testSurroundimgsCoord() { + let target = [ + "0*0", "0*1", "0*2", "0*3", + "1*0", "1*1", "1*2", "1*3", + "2*0", "2*1", "2*2", "2*3", + "3*0", "3*1", "3*2", "3*3", + "4*0", "4*1", "4*2", "4*3" + ] + let a: Array2D = .init(height: 5, width: 4, elements: target) + do { + let surroundList = a.surroundings(around: (1, 1)) + let expectedList = [(1, 2), (0, 2), (0, 1), (0, 0), (1, 0), (2, 0), (2, 1), (2, 2)] + for (i, (r, c)) in surroundList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let surroundList = a.surroundings(around: (0, 1)) + let expectedList = [(0, 2), (0, 0), (1, 0), (1, 1), (1, 2)] + for (i, (r, c)) in surroundList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let surroundList = a.surroundings(around: (0, 0)) + let expectedList = [(0, 1), (1, 0), (1, 1)] + for (i, (r, c)) in surroundList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + do { + let surroundList = a.surroundings(around: (0, 0), ignoreOutside: false) + let expectedList = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)] + for (i, (r, c)) in surroundList.enumerated() { + XCTAssertEqual(r, expectedList[i].0) + XCTAssertEqual(c, expectedList[i].1) + } + } + } + func testSeekWords() { let strs = [ "asdfghjk", From 0452623d25f9ec4121f47776dc71a47346ea402e Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 25 Jun 2023 00:52:19 +0900 Subject: [PATCH 11/17] =?UTF-8?q?=E6=9D=A1=E4=BB=B6=E3=82=92=E6=BA=80?= =?UTF-8?q?=E3=81=9F=E3=81=99=E5=BA=A7=E6=A8=99=E3=82=92=E3=81=8B=E3=81=88?= =?UTF-8?q?=E3=81=99=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 8 +++++++ Tests/AtCoderSupportTests/Array2DTests.swift | 22 ++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 693a385..374a660 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -81,6 +81,14 @@ struct Array2D: Sequence, CustomStringConvertible { } return res } + func locationList(condition: (Element) -> Bool) -> [Coord] { + var res = [Coord]() + for (i, e) in elements.enumerated() { + guard condition(e) else { continue } + res.append((row: i / width, i % width)) + } + return res + } var description: String { var result: String = "" for r in rowRange { diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index b2f5992..8a804eb 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -227,6 +227,28 @@ final class Array2DTests: XCTestCase { } } + func testLocationList() { + let strs = [ + "........", + "..*.....", + "........", + ".*......", + ".....*..", + "........" + ] + let a: Array2D = .init(height: strs.count, width: strs[0].count, stringArray: strs) + do { + let list = a.locationList(condition: { $0 == "*" }) + XCTAssertTrue(list.contains { row, col in row == 1 && col == 2 }) + XCTAssertTrue(list.contains { row, col in row == 3 && col == 1 }) + XCTAssertTrue(list.contains { row, col in row == 4 && col == 5 }) + } + do { + let list = a.locationList(condition: { $0 == "X" }) + XCTAssertTrue(list.isEmpty) + } + } + func testSeekWords() { let strs = [ "asdfghjk", From 7b7a305bcb56109d4cfb19ecc4fec93694cb8a92 Mon Sep 17 00:00:00 2001 From: log5r Date: Sat, 25 Nov 2023 17:08:47 +0900 Subject: [PATCH 12/17] add rotate for Array2D --- Sources/AtCoderSupport/Array2D.swift | 25 ++- Tests/AtCoderSupportTests/Array2DTests.swift | 160 +++++++++++++++++++ 2 files changed, 184 insertions(+), 1 deletion(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 374a660..53a364a 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -6,6 +6,7 @@ struct Array2D: Sequence, CustomStringConvertible { let width: Int let height: Int private(set) var elements: [Element] + private(set) var rotation: Int = 0 let outside: Element? init(height: Int, width: Int, elements: [Element], outside: Element? = nil) { precondition(elements.count == width * height) @@ -22,11 +23,29 @@ struct Array2D: Sequence, CustomStringConvertible { var colRange: Range { 0 ..< width } private func boardContains(coord: Coord) -> Bool { rowRange.contains(coord.row) && colRange.contains(coord.col) } private func boardNotContains(coord: Coord) -> Bool { !boardContains(coord: coord) } - private func indexAt(r: Int, c: Int) -> Int? { + private func indexOriginAt(r: Int, c: Int) -> Int? { guard rowRange.contains(r) else { return nil } guard colRange.contains(c) else { return nil } return r * width + c } + private func indexAt(r: Int, c: Int) -> Int? { + switch rotation { + case 0: + return indexOriginAt(r: r, c: c) + case 1: + guard colRange.contains(r) else { return nil } + guard rowRange.contains(c) else { return nil } + return indexOriginAt(r: height - 1 - c, c: r) + case 2: + return indexOriginAt(r: height - 1 - r, c: width - 1 - c) + case 3: + guard colRange.contains(r) else { return nil } + guard rowRange.contains(c) else { return nil } + return indexOriginAt(r: c, c: width - 1 - r) + default: + fatalError("illegal rotation value: \(rotation)") + } + } subscript(r: Int, c: Int) -> Element { get { guard let i = indexAt(r: r, c: c) else { @@ -89,6 +108,10 @@ struct Array2D: Sequence, CustomStringConvertible { } return res } + mutating func rotate(count: Int = 1) { + rotation += count + rotation = rotation % 4 + } var description: String { var result: String = "" for r in rowRange { diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index 8a804eb..4e5312d 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -249,6 +249,166 @@ final class Array2DTests: XCTestCase { } } + func testRotateOnce() { + let target = [ + "0_0", "0_1", "0_2", "0_3", + "1_0", "1_1", "1_2", "1_3", + "2_0", "2_1", "2_2", "2_3", + "3_0", "3_1", "3_2", "3_3", + "4_0", "4_1", "4_2", "4_3", + "5_0", "5_1", "5_2", "5_3", + "6_0", "6_1", "6_2", "6_3" + ] + var a: Array2D = .init(height: 7, width: 4, elements: target) + a.rotate() + + XCTAssertEqual(a[0, 0], "6_0") + XCTAssertEqual(a[0, 1], "5_0") + XCTAssertEqual(a[0, 2], "4_0") + XCTAssertEqual(a[0, 3], "3_0") + XCTAssertEqual(a[0, 4], "2_0") + XCTAssertEqual(a[0, 5], "1_0") + XCTAssertEqual(a[0, 6], "0_0") + + XCTAssertEqual(a[1, 0], "6_1") + XCTAssertEqual(a[1, 1], "5_1") + XCTAssertEqual(a[1, 2], "4_1") + XCTAssertEqual(a[1, 3], "3_1") + XCTAssertEqual(a[1, 4], "2_1") + XCTAssertEqual(a[1, 5], "1_1") + XCTAssertEqual(a[1, 6], "0_1") + + XCTAssertEqual(a[2, 0], "6_2") + XCTAssertEqual(a[2, 1], "5_2") + XCTAssertEqual(a[2, 2], "4_2") + XCTAssertEqual(a[2, 3], "3_2") + XCTAssertEqual(a[2, 4], "2_2") + XCTAssertEqual(a[2, 5], "1_2") + XCTAssertEqual(a[2, 6], "0_2") + + XCTAssertEqual(a[3, 0], "6_3") + XCTAssertEqual(a[3, 1], "5_3") + XCTAssertEqual(a[3, 2], "4_3") + XCTAssertEqual(a[3, 3], "3_3") + XCTAssertEqual(a[3, 4], "2_3") + XCTAssertEqual(a[3, 5], "1_3") + XCTAssertEqual(a[3, 6], "0_3") + } + + func testRotateTwice() { + let target = [ + "0_0", "0_1", "0_2", "0_3", + "1_0", "1_1", "1_2", "1_3", + "2_0", "2_1", "2_2", "2_3", + "3_0", "3_1", "3_2", "3_3", + "4_0", "4_1", "4_2", "4_3", + "5_0", "5_1", "5_2", "5_3", + "6_0", "6_1", "6_2", "6_3" + ] + var a: Array2D = .init(height: 7, width: 4, elements: target) + a.rotate(count: 2) + + XCTAssertEqual(a[0, 0], "6_3") + XCTAssertEqual(a[0, 1], "6_2") + XCTAssertEqual(a[0, 2], "6_1") + XCTAssertEqual(a[0, 3], "6_0") + + XCTAssertEqual(a[1, 0], "5_3") + XCTAssertEqual(a[1, 1], "5_2") + XCTAssertEqual(a[1, 2], "5_1") + XCTAssertEqual(a[1, 3], "5_0") + + XCTAssertEqual(a[2, 0], "4_3") + XCTAssertEqual(a[2, 1], "4_2") + XCTAssertEqual(a[2, 2], "4_1") + XCTAssertEqual(a[2, 3], "4_0") + + XCTAssertEqual(a[3, 0], "3_3") + XCTAssertEqual(a[3, 1], "3_2") + XCTAssertEqual(a[3, 2], "3_1") + XCTAssertEqual(a[3, 3], "3_0") + + XCTAssertEqual(a[4, 0], "2_3") + XCTAssertEqual(a[4, 1], "2_2") + XCTAssertEqual(a[4, 2], "2_1") + XCTAssertEqual(a[4, 3], "2_0") + + XCTAssertEqual(a[5, 0], "1_3") + XCTAssertEqual(a[5, 1], "1_2") + XCTAssertEqual(a[5, 2], "1_1") + XCTAssertEqual(a[5, 3], "1_0") + + XCTAssertEqual(a[6, 0], "0_3") + XCTAssertEqual(a[6, 1], "0_2") + XCTAssertEqual(a[6, 2], "0_1") + XCTAssertEqual(a[6, 3], "0_0") + } + + func testRotateThreeTimes() { + let target = [ + "0_0", "0_1", "0_2", "0_3", + "1_0", "1_1", "1_2", "1_3", + "2_0", "2_1", "2_2", "2_3", + "3_0", "3_1", "3_2", "3_3", + "4_0", "4_1", "4_2", "4_3", + "5_0", "5_1", "5_2", "5_3", + "6_0", "6_1", "6_2", "6_3" + ] + var a: Array2D = .init(height: 7, width: 4, elements: target) + a.rotate(count: 3) + + XCTAssertEqual(a[0, 0], "0_3") + XCTAssertEqual(a[0, 1], "1_3") + XCTAssertEqual(a[0, 2], "2_3") + XCTAssertEqual(a[0, 3], "3_3") + XCTAssertEqual(a[0, 4], "4_3") + XCTAssertEqual(a[0, 5], "5_3") + XCTAssertEqual(a[0, 6], "6_3") + + XCTAssertEqual(a[1, 0], "0_2") + XCTAssertEqual(a[1, 1], "1_2") + XCTAssertEqual(a[1, 2], "2_2") + XCTAssertEqual(a[1, 3], "3_2") + XCTAssertEqual(a[1, 4], "4_2") + XCTAssertEqual(a[1, 5], "5_2") + XCTAssertEqual(a[1, 6], "6_2") + + XCTAssertEqual(a[2, 0], "0_1") + XCTAssertEqual(a[2, 1], "1_1") + XCTAssertEqual(a[2, 2], "2_1") + XCTAssertEqual(a[2, 3], "3_1") + XCTAssertEqual(a[2, 4], "4_1") + XCTAssertEqual(a[2, 5], "5_1") + XCTAssertEqual(a[2, 6], "6_1") + + XCTAssertEqual(a[3, 0], "0_0") + XCTAssertEqual(a[3, 1], "1_0") + XCTAssertEqual(a[3, 2], "2_0") + XCTAssertEqual(a[3, 3], "3_0") + XCTAssertEqual(a[3, 4], "4_0") + XCTAssertEqual(a[3, 5], "5_0") + XCTAssertEqual(a[3, 6], "6_0") + } + + func testRotateFourTimes() { + let target = [ + "0_0", "0_1", "0_2", "0_3", + "1_0", "1_1", "1_2", "1_3", + "2_0", "2_1", "2_2", "2_3", + "3_0", "3_1", "3_2", "3_3", + "4_0", "4_1", "4_2", "4_3", + "5_0", "5_1", "5_2", "5_3", + "6_0", "6_1", "6_2", "6_3" + ] + var a: Array2D = .init(height: 7, width: 4, elements: target) + a.rotate(count: 4) + for r in 0..<7 { + for c in 0..<4 { + XCTAssertEqual(a[r, c], "\(r)_\(c)") + } + } + } + func testSeekWords() { let strs = [ "asdfghjk", From cd6232c030567effe6a97419f114d472584da8d3 Mon Sep 17 00:00:00 2001 From: log5r Date: Sat, 25 Nov 2023 17:55:40 +0900 Subject: [PATCH 13/17] =?UTF-8?q?=E5=9B=9E=E8=BB=A2=E6=99=82=E3=81=AE?= =?UTF-8?q?=E5=B9=85=E3=81=A8=E9=AB=98=E3=81=95=E3=81=8C=E5=A4=89=E3=82=8F?= =?UTF-8?q?=E3=82=8B=E3=81=AE=E3=82=92=E5=BF=98=E3=82=8C=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=81=A7=E8=BF=BD=E5=8A=A0=E3=81=A7=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 34 ++++++++++++-------- Tests/AtCoderSupportTests/Array2DTests.swift | 10 ++++++ 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 53a364a..dbbe28b 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -3,15 +3,21 @@ struct Array2D: Sequence, CustomStringConvertible { let quadDirectionUnitVec = [(0, 1), (-1, 0), (0, -1), (1, 0)] let octaDirectionUnitVec = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)] - let width: Int - let height: Int + let originWidth: Int + let originHeight: Int + var width: Int { + rotation.isMultiple(of: 2) ? originWidth : originHeight + } + var height: Int { + rotation.isMultiple(of: 2) ? originHeight : originWidth + } private(set) var elements: [Element] private(set) var rotation: Int = 0 let outside: Element? init(height: Int, width: Int, elements: [Element], outside: Element? = nil) { precondition(elements.count == width * height) - self.width = width - self.height = height + self.originWidth = width + self.originHeight = height self.elements = elements self.outside = outside } @@ -19,14 +25,14 @@ struct Array2D: Sequence, CustomStringConvertible { self.init(height: height, width: width, elements: [Element](repeating: element, count: width * height), outside: outside) } var count: Int { elements.count } - var rowRange: Range { 0 ..< height } - var colRange: Range { 0 ..< width } + var rowRange: Range { 0 ..< originHeight } + var colRange: Range { 0 ..< originWidth } private func boardContains(coord: Coord) -> Bool { rowRange.contains(coord.row) && colRange.contains(coord.col) } private func boardNotContains(coord: Coord) -> Bool { !boardContains(coord: coord) } private func indexOriginAt(r: Int, c: Int) -> Int? { guard rowRange.contains(r) else { return nil } guard colRange.contains(c) else { return nil } - return r * width + c + return r * originWidth + c } private func indexAt(r: Int, c: Int) -> Int? { switch rotation { @@ -35,13 +41,13 @@ struct Array2D: Sequence, CustomStringConvertible { case 1: guard colRange.contains(r) else { return nil } guard rowRange.contains(c) else { return nil } - return indexOriginAt(r: height - 1 - c, c: r) + return indexOriginAt(r: originHeight - 1 - c, c: r) case 2: - return indexOriginAt(r: height - 1 - r, c: width - 1 - c) + return indexOriginAt(r: originHeight - 1 - r, c: originWidth - 1 - c) case 3: guard colRange.contains(r) else { return nil } guard rowRange.contains(c) else { return nil } - return indexOriginAt(r: c, c: width - 1 - r) + return indexOriginAt(r: c, c: originWidth - 1 - r) default: fatalError("illegal rotation value: \(rotation)") } @@ -72,7 +78,7 @@ struct Array2D: Sequence, CustomStringConvertible { elements.makeIterator() } func map(_ transform: (Element) throws -> T) rethrows -> Array2D { - try Array2D(height: height, width: width, elements: elements.map(transform)) + try Array2D(height: originHeight, width: originWidth, elements: elements.map(transform)) } func neighbours(around now: Coord, ignoreOutside: Bool = true) -> [Coord] { var res = [Coord]() @@ -104,7 +110,7 @@ struct Array2D: Sequence, CustomStringConvertible { var res = [Coord]() for (i, e) in elements.enumerated() { guard condition(e) else { continue } - res.append((row: i / width, i % width)) + res.append((row: i / originWidth, i % originWidth)) } return res } @@ -148,8 +154,8 @@ extension Array2D where Element == Character { } func seek(word: String) -> [Coord]? { let wcs = Array(word) - for row in 0.. = .init(height: 7, width: 4, elements: target) + XCTAssertEqual(a.width, 4) + a.rotate() + XCTAssertEqual(a.width, 7) + a.rotate() + XCTAssertEqual(a.width, 4) + } + func testRotateOnce() { let target = [ "0_0", "0_1", "0_2", "0_3", From 39f707a1c98ac0ccda72edde1ffa27fa8e217ddd Mon Sep 17 00:00:00 2001 From: log5r Date: Sat, 25 Nov 2023 20:46:31 +0900 Subject: [PATCH 14/17] =?UTF-8?q?=E3=83=88=E3=83=AA=E3=83=A0=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 39 ++++++++++++++++- Tests/AtCoderSupportTests/Array2DTests.swift | 45 ++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index dbbe28b..5a0fc14 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -3,8 +3,8 @@ struct Array2D: Sequence, CustomStringConvertible { let quadDirectionUnitVec = [(0, 1), (-1, 0), (0, -1), (1, 0)] let octaDirectionUnitVec = [(0, 1), (-1, 1), (-1, 0), (-1, -1), (0, -1), (1, -1), (1, 0), (1, 1)] - let originWidth: Int - let originHeight: Int + private(set) var originWidth: Int + private(set) var originHeight: Int var width: Int { rotation.isMultiple(of: 2) ? originWidth : originHeight } @@ -132,6 +132,41 @@ struct Array2D: Sequence, CustomStringConvertible { return result } } +extension Array2D where Element: Equatable { + func trimmed(removing e: Element) -> Array2D { + let tb = trimmedBoard(removing: e) + return Array2D(height: tb.height, width: tb.width, elements: tb.elements) + } + mutating func trim(removing e: Element) { + let tb = trimmedBoard(removing: e) + self.elements = tb.elements + self.originWidth = tb.width + self.originHeight = tb.height + } + private func trimmedBoard(removing e: Element) -> (elements: [Element], width: Int, height: Int) { + var left = width - 1, right = 0, top = height - 1, bottom = 0 + // Swift.min と Collection.min が衝突してて Linux 環境上での世話がめんどいので車輪を再発明 + func vmin(_ a: Int, _ b: Int) -> Int { a < b ? a : b } + func vmax(_ a: Int, _ b: Int) -> Int { a > b ? a : b } + for r in 0.. = .init(height: board.count, width: board[0].count, stringArray: board) + a.trim(removing: ".") + var b: Array2D = .init(height: compact.count, width: compact[0].count, stringArray: compact) + + XCTAssertEqual(a.description, b.description) + + } + + func testTrimed() { + let board = [ + "........", + ".#####..", + "....#...", + "..###...", + "..#.....", + "........" + ] + let compact = [ + "#####", + "...#.", + ".###.", + ".#...", + ] + var a: Array2D = .init(height: board.count, width: board[0].count, stringArray: board) + var b: Array2D = .init(height: compact.count, width: compact[0].count, stringArray: compact) + + XCTAssertEqual(a.trimmed(removing: ".").description, b.description) + + } + func testSeekWords() { let strs = [ "asdfghjk", From 0b35405d3a4db7b4897ec30ea0afb2cbb227fa76 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 26 Nov 2023 13:32:34 +0900 Subject: [PATCH 15/17] =?UTF-8?q?rotate=E3=82=92=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E5=B0=8E=E7=B7=9A?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 5a0fc14..7186d2f 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -118,6 +118,9 @@ struct Array2D: Sequence, CustomStringConvertible { rotation += count rotation = rotation % 4 } + mutating func resetAndRotate(count: Int) { + rotation = count % 4 + } var description: String { var result: String = "" for r in rowRange { From d92abdb64eeb0bfc267b7ae46aa0de100ce3d0a5 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 26 Nov 2023 13:49:43 +0900 Subject: [PATCH 16/17] =?UTF-8?q?=E5=90=8D=E5=89=8D=E3=81=8C=E7=B4=9B?= =?UTF-8?q?=E3=82=89=E3=82=8F=E3=81=97=E3=81=84=E3=81=AE=E3=81=A7=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Sources/AtCoderSupport/Array2D.swift | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Sources/AtCoderSupport/Array2D.swift b/Sources/AtCoderSupport/Array2D.swift index 7186d2f..313ce29 100644 --- a/Sources/AtCoderSupport/Array2D.swift +++ b/Sources/AtCoderSupport/Array2D.swift @@ -25,13 +25,13 @@ struct Array2D: Sequence, CustomStringConvertible { self.init(height: height, width: width, elements: [Element](repeating: element, count: width * height), outside: outside) } var count: Int { elements.count } - var rowRange: Range { 0 ..< originHeight } - var colRange: Range { 0 ..< originWidth } - private func boardContains(coord: Coord) -> Bool { rowRange.contains(coord.row) && colRange.contains(coord.col) } + var originRowRange: Range { 0 ..< originHeight } + var originColRange: Range { 0 ..< originWidth } + private func boardContains(coord: Coord) -> Bool { originRowRange.contains(coord.row) && originColRange.contains(coord.col) } private func boardNotContains(coord: Coord) -> Bool { !boardContains(coord: coord) } private func indexOriginAt(r: Int, c: Int) -> Int? { - guard rowRange.contains(r) else { return nil } - guard colRange.contains(c) else { return nil } + guard originRowRange.contains(r) else { return nil } + guard originColRange.contains(c) else { return nil } return r * originWidth + c } private func indexAt(r: Int, c: Int) -> Int? { @@ -39,14 +39,14 @@ struct Array2D: Sequence, CustomStringConvertible { case 0: return indexOriginAt(r: r, c: c) case 1: - guard colRange.contains(r) else { return nil } - guard rowRange.contains(c) else { return nil } + guard originColRange.contains(r) else { return nil } + guard originRowRange.contains(c) else { return nil } return indexOriginAt(r: originHeight - 1 - c, c: r) case 2: return indexOriginAt(r: originHeight - 1 - r, c: originWidth - 1 - c) case 3: - guard colRange.contains(r) else { return nil } - guard rowRange.contains(c) else { return nil } + guard originColRange.contains(r) else { return nil } + guard originRowRange.contains(c) else { return nil } return indexOriginAt(r: c, c: originWidth - 1 - r) default: fatalError("illegal rotation value: \(rotation)") @@ -123,8 +123,8 @@ struct Array2D: Sequence, CustomStringConvertible { } var description: String { var result: String = "" - for r in rowRange { - for c in colRange { + for r in originRowRange { + for c in originColRange { if c > 0 { result.append(" ") } @@ -173,8 +173,8 @@ extension Array2D where Element: Equatable { extension Array2D where Element: CustomStringConvertible { var description: String { var result: String = "" - for r in rowRange { - for c in colRange { + for r in originRowRange { + for c in originColRange { if c > 0 { result.append(" ") } From 06188a16e5a12d51b053add305691b803fd1ab24 Mon Sep 17 00:00:00 2001 From: log5r Date: Sun, 26 Nov 2023 13:50:18 +0900 Subject: [PATCH 17/17] =?UTF-8?q?testResetAndRotate=E3=81=AE=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E5=BF=98=E3=82=8C=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=81=A7=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tests/AtCoderSupportTests/Array2DTests.swift | 47 ++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Tests/AtCoderSupportTests/Array2DTests.swift b/Tests/AtCoderSupportTests/Array2DTests.swift index 83b0298..d1471e3 100644 --- a/Tests/AtCoderSupportTests/Array2DTests.swift +++ b/Tests/AtCoderSupportTests/Array2DTests.swift @@ -419,6 +419,53 @@ final class Array2DTests: XCTestCase { } } + func testResetAndRotate() { + let target = [ + "0_0", "0_1", "0_2", "0_3", + "1_0", "1_1", "1_2", "1_3", + "2_0", "2_1", "2_2", "2_3", + "3_0", "3_1", "3_2", "3_3", + "4_0", "4_1", "4_2", "4_3", + "5_0", "5_1", "5_2", "5_3", + "6_0", "6_1", "6_2", "6_3" + ] + var a: Array2D = .init(height: 7, width: 4, elements: target) + a.rotate(count: 2) + a.resetAndRotate(count: 1) + + XCTAssertEqual(a[0, 0], "6_0") + XCTAssertEqual(a[0, 1], "5_0") + XCTAssertEqual(a[0, 2], "4_0") + XCTAssertEqual(a[0, 3], "3_0") + XCTAssertEqual(a[0, 4], "2_0") + XCTAssertEqual(a[0, 5], "1_0") + XCTAssertEqual(a[0, 6], "0_0") + + XCTAssertEqual(a[1, 0], "6_1") + XCTAssertEqual(a[1, 1], "5_1") + XCTAssertEqual(a[1, 2], "4_1") + XCTAssertEqual(a[1, 3], "3_1") + XCTAssertEqual(a[1, 4], "2_1") + XCTAssertEqual(a[1, 5], "1_1") + XCTAssertEqual(a[1, 6], "0_1") + + XCTAssertEqual(a[2, 0], "6_2") + XCTAssertEqual(a[2, 1], "5_2") + XCTAssertEqual(a[2, 2], "4_2") + XCTAssertEqual(a[2, 3], "3_2") + XCTAssertEqual(a[2, 4], "2_2") + XCTAssertEqual(a[2, 5], "1_2") + XCTAssertEqual(a[2, 6], "0_2") + + XCTAssertEqual(a[3, 0], "6_3") + XCTAssertEqual(a[3, 1], "5_3") + XCTAssertEqual(a[3, 2], "4_3") + XCTAssertEqual(a[3, 3], "3_3") + XCTAssertEqual(a[3, 4], "2_3") + XCTAssertEqual(a[3, 5], "1_3") + XCTAssertEqual(a[3, 6], "0_3") + } + func testTrim() { let board = [ "........",