diff --git a/Sources/SwiftMathRender/MathRender/MTMathAtomFactory.swift b/Sources/SwiftMathRender/MathRender/MTMathAtomFactory.swift index 6caabb9..8ca2800 100644 --- a/Sources/SwiftMathRender/MathRender/MTMathAtomFactory.swift +++ b/Sources/SwiftMathRender/MathRender/MTMathAtomFactory.swift @@ -580,10 +580,7 @@ public class MTMathAtomFactory { } if let atom = supportedLatexSymbols[name] { - // FIXME: A kludge - objects should be copied here - if name == "int" { return MTMathAtomFactory.operatorWithName( "\u{222B}", limits: false) } - if name == "sum" { return MTMathAtomFactory.operatorWithName( "\u{2211}", limits: true) } - return atom + return atom.copy() } return nil diff --git a/Sources/SwiftMathRender/MathRender/MTMathList.swift b/Sources/SwiftMathRender/MathRender/MTMathList.swift index 72a3e10..2444798 100644 --- a/Sources/SwiftMathRender/MathRender/MTMathList.swift +++ b/Sources/SwiftMathRender/MathRender/MTMathList.swift @@ -78,11 +78,25 @@ public enum MTFontStyle:Int { boldItalic } -public class MTMathAtom: NSObject, NSCopying { - - public var type: MTMathAtomType - public var subScript: MTMathList? - public var superScript: MTMathList? +public class MTMathAtom: NSObject { + + public var type = MTMathAtomType.ordinary + public var subScript: MTMathList? { + didSet { + if subScript != nil && !self.isScriptAllowed() { + subScript = nil + NSException(name: NSExceptionName(rawValue: "Error"), reason: "Subscripts not allowed for atom of type \(self.type)").raise() + } + } + } + public var superScript: MTMathList? { + didSet { + if superScript != nil && !self.isScriptAllowed() { + superScript = nil + NSException(name: NSExceptionName(rawValue: "Error"), reason: "Superscripts not allowed for atom of type \(self.type)").raise() + } + } + } public var nucleus: String = "" public var childAtoms = [MTMathAtom]() // atoms that fused to create this one public var indexRange = NSRange(location: 0, length: 0) // indexRange in list that this atom tracks: @@ -90,43 +104,53 @@ public class MTMathAtom: NSObject, NSCopying { var fontStyle: MTFontStyle = .defaultStyle var fusedAtoms: MTMathList? - public func copy(with zone: NSZone? = nil) -> Any { - let atom = MTMathAtom.atom(withType: type, value: nucleus) - atom.type = self.type - atom.nucleus = self.nucleus - atom.subScript = self.subScript?.copy() as? MTMathList - atom.superScript = self.subScript?.copy() as? MTMathList - atom.indexRange = self.indexRange - atom.fontStyle = self.fontStyle - return atom + init(_ atom:MTMathAtom?) { + guard let atom = atom else { return } + self.type = atom.type + self.nucleus = atom.nucleus + self.subScript = MTMathList(atom.subScript) + self.superScript = MTMathList(atom.superScript) + self.indexRange = atom.indexRange + self.fontStyle = atom.fontStyle + self.childAtoms = [MTMathAtom](atom.childAtoms) + self.fusedAtoms = MTMathList(atom.fusedAtoms) } - public static func atom(withType type:MTMathAtomType, value:String) -> MTMathAtom { - switch type { + override init() { } + + init(type:MTMathAtomType, value:String) { + self.type = type + self.nucleus = type == .radical ? "" : value + } + + public func copy() -> MTMathAtom { + switch self.type { case .largeOperator: - return MTLargeOperator(value: value, limits: true) + return MTLargeOperator(self as? MTLargeOperator) case .fraction: - return MTFraction() + return MTFraction(self as? MTFraction) case .radical: - return MTRadical() - case .placeholder: - return MTMathAtom(type: type, value: UnicodeSymbol.whiteSquare) + return MTRadical(self as? MTRadical) + case .style: + return MTMathStyle(self as? MTMathStyle) case .inner: - return MTInner() + return MTInner(self as? MTInner) case .underline: - return MTUnderLine() + return MTUnderLine(self as? MTUnderLine) case .overline: - return MTOverLine() + return MTOverLine(self as? MTOverLine) case .accent: - return MTAccent(value: value) + return MTAccent(self as? MTAccent) case .space: - return MTMathSpace(space: 0) + return MTMathSpace(self as? MTMathSpace) case .color: - return MTMathColor() + return MTMathColor(self as? MTMathColor) case .colorBox: - return MTMathColorbox() + return MTMathColorbox(self as? MTMathColorbox) + case .table: + return MTMathTable(self as! MTMathTable) default: - return MTMathAtom(type: type, value: value) + return MTMathAtom(self) } } @@ -148,7 +172,6 @@ public class MTMathAtom: NSObject, NSCopying { public override var description: String { var string = "" - string += self.nucleus if self.superScript != nil { string += "^{\(self.superScript!.description)}" @@ -156,7 +179,6 @@ public class MTMathAtom: NSObject, NSCopying { if self.subScript != nil { string += "_{\(self.subScript!.description)}" } - return string } @@ -213,12 +235,6 @@ public class MTMathAtom: NSObject, NSCopying { } func isScriptAllowed() -> Bool { self.type.isScriptAllowed() } - - public init(type: MTMathAtomType, value: String) { - self.type = type - self.nucleus = value - } - func isNotBinaryOperator() -> Bool { self.type.isNotBinaryOperator() } } @@ -232,17 +248,23 @@ public class MTFraction: MTMathAtom { public var hasRule: Bool = true public var leftDelimiter: String? public var rightDelimiter: String? - public var numerator: MTMathList? = MTMathList() - public var denominator: MTMathList? = MTMathList() + public var numerator: MTMathList? + public var denominator: MTMathList? - public override func copy(with zone: NSZone? = nil) -> Any { - let frac = super.copy(with: zone) as! MTFraction - frac.numerator = self.numerator?.copy() as? MTMathList - frac.denominator = self.denominator?.copy() as? MTMathList - frac.hasRule = self.hasRule - frac.leftDelimiter = self.leftDelimiter - frac.rightDelimiter = self.rightDelimiter - return frac + init(_ frac: MTFraction?) { + super.init(frac) + self.type = .fraction + self.numerator = MTMathList(frac?.numerator) + self.denominator = MTMathList(frac?.denominator) + self.hasRule = frac?.hasRule ?? true + self.leftDelimiter = frac?.leftDelimiter + self.rightDelimiter = frac?.rightDelimiter + } + + init(hasRule rule:Bool = true) { + super.init() + self.type = .fraction + self.hasRule = rule } override public var description: String { @@ -280,28 +302,27 @@ public class MTFraction: MTMathAtom { return finalized } - convenience init(hasRule: Bool = true) { - self.init(type: .fraction, value: "") - self.hasRule = hasRule - } } public class MTRadical: MTMathAtom { // Under the roof - public var radicand: MTMathList? = MTMathList() + public var radicand: MTMathList? // Value on radical sign public var degree: MTMathList? - convenience init() { - self.init(type: .radical, value: "") + init(_ rad:MTRadical?) { + super.init(rad) + self.type = .radical + self.radicand = MTMathList(rad?.radicand) + self.degree = MTMathList(rad?.degree) + self.nucleus = "" } - public override func copy(with zone: NSZone? = nil) -> Any { - let rad = super.copy(with: zone) as! MTRadical - rad.radicand = self.radicand?.copy() as? MTMathList - rad.degree = self.degree?.copy() as? MTMathList - return rad + override init() { + super.init() + self.type = .radical + self.nucleus = "" } override public var description: String { @@ -338,15 +359,17 @@ public class MTRadical: MTMathAtom { public class MTLargeOperator: MTMathAtom { public var limits: Bool = false - init(value: String, limits: Bool) { - super.init(type: .largeOperator, value: value) - self.limits = limits + init(_ op:MTLargeOperator?) { + super.init(op) + self.type = .largeOperator + self.limits = op?.limits ?? false } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTLargeOperator - op.limits = self.limits - return op + init(value: String, limits: Bool) { + super.init() + self.type = .largeOperator + self.nucleus = value + self.limits = limits } } @@ -357,36 +380,31 @@ public class MTInner: MTMathAtom { public var leftBoundary: MTMathAtom? { didSet { if leftBoundary != nil && leftBoundary!.type != .boundary { - assertionFailure("Left boundary must be of type .boundary") + leftBoundary = nil + NSException(name: NSExceptionName(rawValue: "Error"), reason: "Left boundary must be of type .boundary").raise() } } } public var rightBoundary: MTMathAtom? { didSet { if rightBoundary != nil && rightBoundary!.type != .boundary { - assertionFailure("Right boundary must be of type .boundary") + rightBoundary = nil + NSException(name: NSExceptionName(rawValue: "Error"), reason: "Right boundary must be of type .boundary").raise() } } } - public override func copy(with zone: NSZone? = nil) -> Any { - let inner = super.copy(with: zone) as! MTInner - inner.innerList = self.innerList?.copy() as? MTMathList - inner.leftBoundary = self.leftBoundary?.copy() as? MTMathAtom - inner.rightBoundary = self.rightBoundary?.copy() as? MTMathAtom - return inner + init(_ inner:MTInner?) { + super.init(inner) + self.type = .inner + self.innerList = MTMathList(inner?.innerList) + self.leftBoundary = MTMathAtom(inner?.leftBoundary) + self.rightBoundary = MTMathAtom(inner?.rightBoundary) } - init() { - super.init(type: .inner, value: "") - } - - public override convenience init(type: MTMathAtomType, value: String) { - if type == .inner { - self.init(); return - } - assertionFailure("MTInner(type:value:) cannot be called. Use MTInner() instead.") - self.init() + override init() { + super.init() + self.type = .inner } override public var description: String { @@ -427,14 +445,15 @@ public class MTOverLine: MTMathAtom { return finalized } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTOverLine - op.innerList = self.innerList?.copy() as? MTMathList - return op + init(_ over: MTOverLine?) { + super.init(over) + self.type = .overline + self.innerList = MTMathList(self.innerList) } - convenience init() { - self.init(type: .overline, value: "") + override init() { + super.init() + self.type = .overline } } @@ -447,14 +466,15 @@ public class MTUnderLine: MTMathAtom { return finalized } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTUnderLine - op.innerList = self.innerList?.copy() as? MTMathList - return op + init(_ under: MTUnderLine?) { + super.init(under) + self.type = .underline + self.innerList = MTMathList(under?.innerList) } - convenience init() { - self.init(type: .underline, value: "") + override init() { + super.init() + self.type = .underline } } @@ -467,31 +487,33 @@ public class MTAccent: MTMathAtom { return finalized } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTAccent - op.innerList = self.innerList?.copy() as? MTMathList - return op + init(_ accent: MTAccent?) { + super.init(accent) + self.type = .accent + self.innerList = MTMathList(accent?.innerList) } - convenience init(value: String) { - self.init(type: .accent, value: value) + init(value: String) { + super.init() + self.type = .accent + self.nucleus = value } } public class MTMathSpace: MTMathAtom { public var space: CGFloat = 0 - convenience init(space: CGFloat) { - self.init(type: .space, value: "") + init(_ space: MTMathSpace?) { + super.init(space) + self.type = .space + self.space = space?.space ?? 0 + } + + init(space:CGFloat) { + super.init() + self.type = .space self.space = space } - - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTMathSpace - op.space = self.space - return op - } - } public enum MTLineStyle { @@ -517,15 +539,16 @@ public enum MTLineStyle { public class MTMathStyle: MTMathAtom { public var style: MTLineStyle = .display - convenience init(style: MTLineStyle = .display) { - self.init(type: .style, value: "") - self.style = style + init(_ style:MTMathStyle?) { + super.init(style) + self.type = .style + self.style = style?.style ?? .display } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTMathStyle - op.style = self.style - return op + init(style:MTLineStyle) { + super.init() + self.type = .style + self.style = style } } @@ -533,23 +556,16 @@ public class MTMathColor: MTMathAtom { public var colorString:String="" public var innerList:MTMathList? - init() { - super.init(type: .color, value: "") + init(_ color: MTMathColor?) { + super.init(color) + self.type = .color + self.colorString = color?.colorString ?? "" + self.innerList = MTMathList(color?.innerList) } - public override convenience init(type: MTMathAtomType, value: String) { - if type == .color { - self.init(); return - } - NSException(name: NSExceptionName("InvalidMethod"), reason: "MTMathColor(type:value) cannot be called. Use MTMathColor() instead.").raise() - self.init() - } - - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTMathColor - op.colorString = self.colorString - op.innerList = self.innerList?.copy() as? MTMathList - return op + override init() { + super.init() + self.type = .color } public override var string: String { @@ -561,23 +577,16 @@ public class MTMathColorbox: MTMathAtom { public var colorString:String="" public var innerList:MTMathList? - init() { - super.init(type: .color, value: "") + init(_ cbox: MTMathColorbox?) { + super.init(cbox) + self.type = .colorBox + self.colorString = cbox?.colorString ?? "" + self.innerList = MTMathList(cbox?.innerList) } - public override convenience init(type: MTMathAtomType, value: String) { - if type == .color { - self.init(); return - } - NSException(name: NSExceptionName("InvalidMethod"), reason: "MTMathColorbox(type:value) cannot be called. Use MTMathColorbox() instead.").raise() - self.init() - } - - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTMathColorbox - op.colorString = self.colorString - op.innerList = self.innerList?.copy() as? MTMathList - return op + override init() { + super.init() + self.type = .colorBox } public override var string: String { @@ -611,24 +620,33 @@ public class MTMathTable: MTMathAtom { return finalized } - convenience init(environment: String? = nil) { - self.init(type: .table, value: "") + init(environment: String?) { + super.init() + self.type = .table self.environment = environment } - public override func copy(with zone: NSZone? = nil) -> Any { - let op = super.copy(with: zone) as! MTMathTable - op.interRowAdditionalSpacing = self.interRowAdditionalSpacing - op.interColumnSpacing = self.interColumnSpacing - op.environment = self.environment + init(_ table:MTMathTable) { + super.init(table) + self.type = .table + self.alignments = table.alignments + self.interRowAdditionalSpacing = table.interRowAdditionalSpacing + self.interColumnSpacing = table.interColumnSpacing + self.environment = table.environment var cellCopy = [[MTMathList]]() - cellCopy.reserveCapacity(self.cells.count) - for row in self.cells { - let newRow = [MTMathList](row) + for row in table.cells { + var newRow = [MTMathList]() + for col in row { + newRow.append(MTMathList(col)!) + } cellCopy.append(newRow) } - op.cells = cellCopy - return op + self.cells = cellCopy + } + + override init() { + super.init() + self.type = .table } public func set(cell list: MTMathList, forRow row:Int, column:Int) { @@ -685,12 +703,13 @@ extension MTMathList { public var string: String { self.description } } -public class MTMathList: NSObject, NSCopying { +public class MTMathList : NSObject { - public func copy(with zone: NSZone? = nil) -> Any { - let list = MTMathList() - list.atoms = [MTMathAtom](self.atoms) - return list + init?(_ list:MTMathList?) { + guard let list = list else { return nil } + for atom in list.atoms { + self.atoms.append(atom.copy()) + } } public var atoms = [MTMathAtom]() @@ -753,9 +772,7 @@ public class MTMathList: NSObject, NSCopying { self.atoms.append(atom) } - public override init() { - self.atoms = [] - } + public override init() { super.init() } func NSParamException(_ param:Any?) { if param == nil { diff --git a/Sources/SwiftMathRender/MathRender/MTMathListBuilder.swift b/Sources/SwiftMathRender/MathRender/MTMathListBuilder.swift index adf76ec..08e338b 100644 --- a/Sources/SwiftMathRender/MathRender/MTMathListBuilder.swift +++ b/Sources/SwiftMathRender/MathRender/MTMathListBuilder.swift @@ -546,7 +546,7 @@ public class MTMathListBuilder { return mathColor } else if command == "colorbox" { // A color command has 2 arguments - let mathColorbox = MTMathColorbox(); + let mathColorbox = MTMathColorbox() mathColorbox.colorString = self.readColor()! mathColorbox.innerList = self.buildInternal(true) return mathColorbox diff --git a/Tests/SwiftMathRenderTests/MTMathListTests.swift b/Tests/SwiftMathRenderTests/MTMathListTests.swift index 7940058..ab910e3 100644 --- a/Tests/SwiftMathRenderTests/MTMathListTests.swift +++ b/Tests/SwiftMathRenderTests/MTMathListTests.swift @@ -139,7 +139,7 @@ final class MTMathListTests: XCTestCase { let list = MTMathList() var atom : MTMathAtom? = nil list.add(atom) - atom = MTMathAtom.atom(withType: .boundary, value: "") + atom = MTMathAtom(type: .boundary, value: "") XCTExpectFailure("Test adding an illegal atom", options:options) { XCTAssertThrowsError(list.add(atom)) } @@ -169,7 +169,7 @@ final class MTMathListTests: XCTestCase { let list = MTMathList() var atom : MTMathAtom? = nil list.insert(atom, at: 0) - atom = MTMathAtom.atom(withType: .boundary, value:"") + atom = MTMathAtom(type: .boundary, value:"") XCTExpectFailure("Test adding an illegal atom", options:options) { XCTAssertThrowsError(list.insert(atom, at:0)) } @@ -262,356 +262,363 @@ final class MTMathListTests: XCTestCase { // func MTAssertNotEqual(test, expression1, expression2, ...) \ // _XCTPrimitiveAssertNotEqual(test, expression1, @#expression1, expression2, @#expression2, __VA_ARGS__) -// func checkAtomCopy(_ copy:MTMathAtom, original:MTMathAtom, forTest test:XCTestCase?) throws { -// MTAssertEqual(test, copy.type, original.type); -// MTAssertEqual(test, copy.nucleus, original.nucleus); -// // Deep copy -// MTAssertNotEqual(test, copy, original); -// } -// -// func checkListCopy(_ copy:MTMathList, original:MTMathList, forTest test:XCTestCase?) throws { -// MTAssertEqual(test, copy.atoms.count, original.atoms.count) -// for (i, copyAtom) in copy.atoms.enumerated() { -// let origAtom = original.atoms[i]; -// try self.checkAtomCopy(copyAtom, original:origAtom, forTest:test) -// } -// } -// -// func testCopy() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = list.copy() -// checkListCopy(list2, original:list, forTest:self) -// } -// -// func testAtomInit() throws { -// var atom = MTMathAtom.atom(withType: .open, value:"(") -// XCTAssertEqual(atom.nucleus, "("); -// XCTAssertEqual(atom.type, .open); -// -// atom = MTMathAtom.atom(withType: .radical, value:"(") -// XCTAssertEqual(atom.nucleus, ""); -// XCTAssertEqual(atom.type, .radical); -// } -// -// func testAtomScripts() throws { -// var atom = MTMathAtom.atom(withType: .open, value:"(") -// XCTAssertTrue(atom.isScriptAllowed()) -// atom.subScript = MTMathList() -// XCTAssertNotNil(atom.subScript); -// atom.superScript = MTMathList() -// XCTAssertNotNil(atom.superScript); -// -// atom = MTMathAtom.atom(withType: .boundary, value:"(") -// XCTAssertFalse(atom.isScriptAllowed()); -// // Can set to nil -// atom.subScript = nil; -// XCTAssertNil(atom.subScript); -// atom.superScript = nil; -// XCTAssertNil(atom.superScript); -// // Can't set to value -// let list = MTMathList() -// XCTAssertThrowsError(atom.subScript = list); -// XCTAssertThrowsError(atom.superScript = list); -// } -// -// func testAtomCopy() throws { -// let list = MTMathList() -// let atom1 = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom1) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = MTMathList() -// list2.add(atom3) -// list2.add(atom2) -// -// let atom = MTMathAtom.atom(withType: .open, value:"(") -// atom.subScript = list; -// atom.superScript = list2; -// let copy = atom.copy() -// -// checkAtomCopy(copy, original:atom, forTest:self) -// checkListCopy(copy.superScript, original:atom.superScript, forTest:self) -// checkListCopy(copy.subScript, original:atom.subScript, forTest:self) -// } -// -// func testCopyFraction() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = MTMathList() -// list2.add(atom3) -// list2.add(atom2) -// -// let frac = MTFraction(hasRule: false) -// XCTAssertEqual(frac.type, .fraction); -// frac.numerator = list; -// frac.denominator = list2; -// frac.leftDelimiter = "a"; -// frac.rightDelimiter = "b"; -// -// let copy = frac.copy() as! MTFraction -// try checkAtomCopy(copy, original:frac, forTest:self) -// checkListCopy(copy.numerator, original:frac.numerator, forTest:self) -// checkListCopy(copy.denominator, original:frac.denominator, forTest:self) -// XCTAssertFalse(copy.hasRule) -// XCTAssertEqual(copy.leftDelimiter, "a"); -// XCTAssertEqual(copy.rightDelimiter, "b"); -// } -// -// func testCopyRadical() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = MTMathList() -// list2.add(atom3) -// list2.add(atom2) -// -// let rad = [[MTRadical alloc] init) -// XCTAssertEqual(rad.type, kMTMathAtomRadical); -// rad.radicand = list; -// rad.degree = list2; -// -// let copy = [rad copy) -// checkAtomCopy(copy original:rad forTest:self) -// checkListCopy(copy.radicand original:rad.radicand forTest:self) -// checkListCopy(copy.degree original:rad.degree forTest:self) -// } -// -// func testCopyLargeOperator() throws { -// let lg = [[MTLargeOperator alloc] initWithValue:"lim" limits:true) -// XCTAssertEqual(lg.type, kMTMathAtomLargeOperator); -// XCTAssertTrue(lg.limits); -// -// let copy = [lg copy) -// checkAtomCopy(copy original:lg forTest:self) -// XCTAssertEqual(copy.limits, lg.limits); -// } -// func testCopyInner() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let inner = MTInner() -// inner.innerList = list; -// inner.leftBoundary = MTMathAtom.atom(withType: .boundary, value: "(") -// inner.rightBoundary = MTMathAtom.atom(withType: .boundary, value:")") -// XCTAssertEqual(inner.type, .inner); -// -// let copy = [inner copy) -// checkAtomCopy(copy original:inner forTest:self) -// checkListCopy(copy.innerList original:inner.innerList forTest:self) -// checkAtomCopy(copy.leftBoundary original:inner.leftBoundary forTest:self) -// checkAtomCopy(copy.rightBoundary original:inner.rightBoundary forTest:self) -// } -// -// func testSetInnerBoundary() throws { -// let inner = MTInner() -// -// // Can set non-nil -// inner.leftBoundary = MTMathAtom.atom(withType: .boundary, value:"(") -// inner.rightBoundary = MTMathAtom.atom(withType: .boundary, value:")") -// XCTAssertNotNil(inner.leftBoundary); -// XCTAssertNotNil(inner.rightBoundary); -// // Can set nil -// inner.leftBoundary = nil; -// inner.rightBoundary = nil; -// XCTAssertNil(inner.leftBoundary); -// XCTAssertNil(inner.rightBoundary); -// // Can't set non boundary -// let atom = MTMathAtomFactory.placeholder() -// XCTAssertThrowsError(inner.leftBoundary = atom); -// XCTAssertThrowsError(inner.rightBoundary = atom); -// } -// -// func testCopyOverline() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let over = MTOverLine() -// XCTAssertEqual(over.type, .overline); -// over.innerList = list; -// -// let copy = [over copy) -// checkAtomCopy(copy original:over forTest:self) -// checkListCopy(copy.innerList original:over.innerList forTest:self) -// } -// -// func testCopyUnderline() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let under = MTUnderLine() -// XCTAssertEqual(under.type, .underline); -// under.innerList = list; -// -// let copy = [under copy) -// checkAtomCopy(copy, original:under, forTest:self) -// checkListCopy(copy.innerList original:under.innerList forTest:self) -// } -// -// func testCopyAcccent() throws { -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let accent = [[MTAccent alloc] initWithValue:"^") -// XCTAssertEqual(accent.type, kMTMathAtomAccent); -// accent.innerList = list; -// -// let copy = [accent copy) -// checkAtomCopy(copy original:accent forTest:self) -// checkListCopy(copy.innerList original:accent.innerList forTest:self) -// } -// -// func testCopySpace() throws { -// let space = MTMathSpace(space: 3) -// XCTAssertEqual(space.type, .space); -// -// let copy = [space copy) -// checkAtomCopy(copy, original:space, forTest:self) -// XCTAssertEqual(space.space, copy.space); -// } -// -// func testCopyStyle() throws { -// let style = MTMathStyle(style: .script) -// XCTAssertEqual(style.type, .style); -// -// let copy = style.copy() as! MTMathStyle -// checkAtomCopy(copy, original:style, forTest:self) -// XCTAssertEqual(style.style, copy.style); -// } -// -// func testCreateMathTable() throws { -// let table = MTMathTable() -// XCTAssertEqual(table.type, .table); -// -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = MTMathList() -// list2.add(atom3) -// list2.add(atom2) -// -// table.set(cell: list, forRow:3, column:2) -// table.set(cell: list2, forRow:1, column:0) -// -// table.set(alignment: .left, forColumn: 2) -// table.set(alignment: .right, forColumn:1) -// -// // Verify that everything is created correctly -// XCTAssertEqual(table.cells.count, 4); // 4 rows -// XCTAssertNotNil(table.cells[0]); -// XCTAssertEqual(table.cells[0].count, 0); // 0 elements in row 0 -// XCTAssertEqual(table.cells[1].count, 1); // 1 element in row 1 -// XCTAssertNotNil(table.cells[2]); -// XCTAssertEqual(table.cells[2].count, 0); -// XCTAssertEqual(table.cells[3].count, 3); -// -// // Verify the elements in the rows -// XCTAssertEqual(table.cells[1][0].atoms.count, 2); -// XCTAssertEqual(table.cells[1][0], list2); -// XCTAssertNotNil(table.cells[3][0]); -// XCTAssertEqual(table.cells[3][0].atoms.count, 0); -// -// XCTAssertNotNil(table.cells[3][0]); -// XCTAssertEqual(table.cells[3][0].atoms.count, 0); -// -// XCTAssertNotNil(table.cells[3][1]); -// XCTAssertEqual(table.cells[3][1].atoms.count, 0); -// -// XCTAssertEqual(table.cells[3][2], list); -// -// XCTAssertEqual(table.numRows, 4); -// XCTAssertEqual(table.numColumns, 3); -// -// // Verify the alignments -// XCTAssertEqual(table.alignments.count, 3); -// XCTAssertEqual(table.alignments[0], .center); -// XCTAssertEqual(table.alignments[1], .right); -// XCTAssertEqual(table.alignments[2], .left); -// } -// -// func testCopyMathTable() throws { -// let table = MTMathTable() -// XCTAssertEqual(table.type, .table); -// -// let list = MTMathList() -// let atom = MTMathAtomFactory.placeholder() -// let atom2 = MTMathAtomFactory.times() -// let atom3 = MTMathAtomFactory.divide() -// list.add(atom) -// list.add(atom2); -// list.add(atom3) -// -// let list2 = MTMathList() -// list2.add(atom3) -// list2.add(atom2) -// -// table.set(cell:list, forRow:0, column:1) -// table.set(cell:list2, forRow:0, column:2) -// -// table.set(alignment: .left, forColumn:2) -// table.set(alignment: .right, forColumn:1) -// table.interRowAdditionalSpacing = 3; -// table.interColumnSpacing = 10; -// -// let copy = table.copy() as! MTMathTable -// try checkAtomCopy(copy, original:table, forTest:self) -// XCTAssertEqual(copy.interColumnSpacing, table.interColumnSpacing); -// XCTAssertEqual(copy.interRowAdditionalSpacing, table.interRowAdditionalSpacing); -// XCTAssertEqual(copy.alignments, table.alignments); -// XCTAssertNotEqual(copy.alignments, table.alignments); -// -// XCTAssertNotEqual(copy.cells, table.cells); -// XCTAssertNotEqual(copy.cells[0], table.cells[0]); -// XCTAssertEqual(copy.cells[0].count, table.cells[0].count); -// XCTAssertEqual(copy.cells[0][0].atoms.count, 0); -// XCTAssertNotEqual(copy.cells[0][0], table.cells[0][0]); -// try checkListCopy(copy.cells[0][1], original:list, forTest:self) -// try checkListCopy(copy.cells[0][2], original:list2, forTest:self) -// } -// + func checkAtomCopy(_ copy:MTMathAtom?, original:MTMathAtom?, forTest test:String) throws { + guard let copy = copy, let original = original else { return } + XCTAssertEqual(copy.type, original.type, test) + XCTAssertEqual(copy.nucleus, original.nucleus, test) + // Should be different objects with the same content + XCTAssertNotEqual(copy, original, test) + } + + func checkListCopy(_ copy:MTMathList?, original:MTMathList?, forTest test:String) throws { + guard let copy = copy, let original = original else { return } + XCTAssertEqual(copy.atoms.count, original.atoms.count, test) + for (i, copyAtom) in copy.atoms.enumerated() { + let origAtom = original.atoms[i]; + try self.checkAtomCopy(copyAtom, original:origAtom, forTest:test) + } + } + + func testCopy() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList(list) + try checkListCopy(list2, original:list, forTest:self.description) + } + + func testAtomInit() throws { + var atom = MTMathAtom(type: .open, value: "(") + XCTAssertEqual(atom.nucleus, "(") + XCTAssertEqual(atom.type, .open) + + atom = MTMathAtom(type: .radical, value:"(") + XCTAssertEqual(atom.nucleus, ""); + XCTAssertEqual(atom.type, .radical); + } + + func testAtomScripts() throws { + var atom = MTMathAtom(type: .open, value:"(") + XCTAssertTrue(atom.isScriptAllowed()) + atom.subScript = MTMathList() + XCTAssertNotNil(atom.subScript); + atom.superScript = MTMathList() + XCTAssertNotNil(atom.superScript); + + atom = MTMathAtom(type: .boundary, value:"(") + XCTAssertFalse(atom.isScriptAllowed()); + // Can set to nil + atom.subScript = nil; + XCTAssertNil(atom.subScript); + atom.superScript = nil; + XCTAssertNil(atom.superScript); + // Can't set to value + let list = MTMathList() + + XCTExpectFailure("No sub/super-script on boundary atoms", options: options) { + XCTAssertThrowsError(atom.subScript = list) + XCTAssertThrowsError(atom.superScript = list) + } + } + + func testAtomCopy() throws { + let list = MTMathList() + let atom1 = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom1) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList() + list2.add(atom3) + list2.add(atom2) + + let atom = MTMathAtom(type: .open, value:"(") + atom.subScript = list; + atom.superScript = list2; + let copy : MTMathAtom = atom.copy() + + try checkAtomCopy(copy, original:atom, forTest:self.description) + try checkListCopy(copy.superScript, original:atom.superScript, forTest:self.description) + try checkListCopy(copy.subScript, original:atom.subScript, forTest:self.description) + } + + func testCopyFraction() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList() + list2.add(atom3) + list2.add(atom2) + + let frac = MTFraction(hasRule: false) + XCTAssertEqual(frac.type, .fraction); + frac.numerator = list; + frac.denominator = list2; + frac.leftDelimiter = "a"; + frac.rightDelimiter = "b"; + + let copy = MTFraction(frac) + try checkAtomCopy(copy, original:frac, forTest:self.description) + try checkListCopy(copy.numerator, original:frac.numerator, forTest:self.description) + try checkListCopy(copy.denominator, original:frac.denominator, forTest:self.description) + XCTAssertFalse(copy.hasRule) + XCTAssertEqual(copy.leftDelimiter, "a"); + XCTAssertEqual(copy.rightDelimiter, "b"); + } + + func testCopyRadical() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList() + list2.add(atom3) + list2.add(atom2) + + let rad = MTRadical() + XCTAssertEqual(rad.type, .radical) + rad.radicand = list; + rad.degree = list2; + + let copy = MTRadical(rad) + try checkAtomCopy(copy, original:rad, forTest:self.description) + try checkListCopy(copy.radicand, original:rad.radicand ,forTest:self.description) + try checkListCopy(copy.degree, original:rad.degree, forTest:self.description) + } + + func testCopyLargeOperator() throws { + let lg = MTLargeOperator(value: "lim", limits:true) + XCTAssertEqual(lg.type, .largeOperator); + XCTAssertTrue(lg.limits); + + let copy = MTLargeOperator(lg) + try checkAtomCopy(copy, original:lg, forTest:self.description) + XCTAssertEqual(copy.limits, lg.limits); + } + + func testCopyInner() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let inner = MTInner() + inner.innerList = list; + inner.leftBoundary = MTMathAtom(type: .boundary, value: "(") + inner.rightBoundary = MTMathAtom(type: .boundary, value:")") + XCTAssertEqual(inner.type, .inner); + + let copy = MTInner(inner) + try checkAtomCopy(copy, original:inner, forTest:self.description) + try checkListCopy(copy.innerList, original:inner.innerList, forTest:self.description) + try checkAtomCopy(copy.leftBoundary!, original:inner.leftBoundary, forTest:self.description) + try checkAtomCopy(copy.rightBoundary, original:inner.rightBoundary, forTest:self.description) + } + + func testSetInnerBoundary() throws { + let inner = MTInner() + + // Can set non-nil + inner.leftBoundary = MTMathAtom(type: .boundary, value:"(") + inner.rightBoundary = MTMathAtom(type: .boundary, value:")") + XCTAssertNotNil(inner.leftBoundary); + XCTAssertNotNil(inner.rightBoundary); + // Can set nil + inner.leftBoundary = nil; + inner.rightBoundary = nil; + XCTAssertNil(inner.leftBoundary); + XCTAssertNil(inner.rightBoundary); + // Can't set non boundary + let atom = MTMathAtomFactory.placeholder() + XCTExpectFailure("Setting illegal boundary atoms", options: options) { + XCTAssertThrowsError(inner.leftBoundary = atom); + XCTAssertThrowsError(inner.rightBoundary = atom); + } + } + + func testCopyOverline() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let over = MTOverLine() + XCTAssertEqual(over.type, .overline); + over.innerList = list; + + let copy = MTOverLine(over) + try checkAtomCopy(copy, original:over, forTest:self.description) + try checkListCopy(copy.innerList, original:over.innerList, forTest:self.description) + } + + func testCopyUnderline() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let under = MTUnderLine() + XCTAssertEqual(under.type, .underline); + under.innerList = list; + + let copy = MTUnderLine(under) + try checkAtomCopy(copy, original:under, forTest:self.description) + try checkListCopy(copy.innerList, original:under.innerList, forTest:self.description) + } + + func testCopyAcccent() throws { + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let accent = MTAccent(value: "^") + XCTAssertEqual(accent.type, .accent); + accent.innerList = list; + + let copy = MTAccent(accent) + try checkAtomCopy(copy, original:accent, forTest:self.description) + try checkListCopy(copy.innerList ,original:accent.innerList, forTest:self.description) + } + + func testCopySpace() throws { + let space = MTMathSpace(space: 3) + XCTAssertEqual(space.type, .space); + + let copy = MTMathSpace(space) + try checkAtomCopy(copy, original:space, forTest:self.description) + XCTAssertEqual(space.space, copy.space); + } + + func testCopyStyle() throws { + let style = MTMathStyle(style: .script) + XCTAssertEqual(style.type, .style); + + let copy = MTMathStyle(style) + try checkAtomCopy(copy, original:style, forTest:self.description) + XCTAssertEqual(style.style, copy.style); + } + + func testCreateMathTable() throws { + let table = MTMathTable() + XCTAssertEqual(table.type, .table); + + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList() + list2.add(atom3) + list2.add(atom2) + + table.set(cell: list, forRow:3, column:2) + table.set(cell: list2, forRow:1, column:0) + + table.set(alignment: .left, forColumn: 2) + table.set(alignment: .right, forColumn:1) + + // Verify that everything is created correctly + XCTAssertEqual(table.cells.count, 4); // 4 rows + XCTAssertNotNil(table.cells[0]); + XCTAssertEqual(table.cells[0].count, 0); // 0 elements in row 0 + XCTAssertEqual(table.cells[1].count, 1); // 1 element in row 1 + XCTAssertNotNil(table.cells[2]); + XCTAssertEqual(table.cells[2].count, 0); + XCTAssertEqual(table.cells[3].count, 3); + + // Verify the elements in the rows + XCTAssertEqual(table.cells[1][0].atoms.count, 2); + XCTAssertEqual(table.cells[1][0], list2); + XCTAssertNotNil(table.cells[3][0]); + XCTAssertEqual(table.cells[3][0].atoms.count, 0); + + XCTAssertNotNil(table.cells[3][0]); + XCTAssertEqual(table.cells[3][0].atoms.count, 0); + + XCTAssertNotNil(table.cells[3][1]); + XCTAssertEqual(table.cells[3][1].atoms.count, 0); + + XCTAssertEqual(table.cells[3][2], list); + + XCTAssertEqual(table.numRows, 4); + XCTAssertEqual(table.numColumns, 3); + + // Verify the alignments + XCTAssertEqual(table.alignments.count, 3); + XCTAssertEqual(table.alignments[0], .center); + XCTAssertEqual(table.alignments[1], .right); + XCTAssertEqual(table.alignments[2], .left); + } + + func testCopyMathTable() throws { + let table = MTMathTable() + XCTAssertEqual(table.type, .table); + + let list = MTMathList() + let atom = MTMathAtomFactory.placeholder() + let atom2 = MTMathAtomFactory.times() + let atom3 = MTMathAtomFactory.divide() + list.add(atom) + list.add(atom2); + list.add(atom3) + + let list2 = MTMathList() + list2.add(atom3) + list2.add(atom2) + + table.set(cell:list, forRow:0, column:1) + table.set(cell:list2, forRow:0, column:2) + + table.set(alignment: .left, forColumn:2) + table.set(alignment: .right, forColumn:1) + table.interRowAdditionalSpacing = 3; + table.interColumnSpacing = 10; + + let copy = MTMathTable(table) + try checkAtomCopy(copy, original:table, forTest:self.description) + XCTAssertEqual(copy.interColumnSpacing, table.interColumnSpacing); + XCTAssertEqual(copy.interRowAdditionalSpacing, table.interRowAdditionalSpacing); + XCTAssertEqual(copy.alignments, table.alignments) + + XCTAssertNotEqual(copy.cells, table.cells); + XCTAssertNotEqual(copy.cells[0], table.cells[0] ); + XCTAssertEqual(copy.cells[0].count, table.cells[0].count); + XCTAssertEqual(copy.cells[0][0].atoms.count, 0); + XCTAssertNotEqual(copy.cells[0][0], table.cells[0][0]); + try checkListCopy(copy.cells[0][1], original:list, forTest:self.description) + try checkListCopy(copy.cells[0][2], original:list2, forTest:self.description) + } + }