{"action":"created","comment":{"url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/comments/87850297","pull_request_review_id":8430106,"id":87850297,"diff_hunk":"@@ -0,0 +1,203 @@\n+//\n+//  CardType.swift\n+//  CardEntry\n+//\n+//  Created by Jason Clark on 6/28/16.\n+//  Copyright © 2016 Raizlabs. All rights reserved.\n+//\n+\n+import Foundation\n+\n+//MARK: - CardType\n+\n+enum CardType {\n+    case visa\n+    case masterCard\n+    case amex\n+    case diners\n+    case discover\n+    case jcb\n+\n+    static let allValues: [CardType] = [.visa, .masterCard, .amex, .diners, .discover, .jcb]\n+\n+    fileprivate var validationRequirements: ValidationRequirement {\n+        var prefix = [PrefixContainable](), length = [Int]()\n+\n+        switch self {\n+        /* // IIN prefixes and length requriements retreived from https://en.wikipedia.org/wiki/Bank_card_number on June 28, 2016 */\n+\n+        case .visa:         prefix = [\"4\"]\n+                            length = [13, 16, 19]\n+\n+        case .masterCard:   prefix = [\"51\"...\"55\", \"2221\"...\"2720\"]\n+                            length = [16]\n+\n+        case .amex:         prefix = [\"34\", \"37\"]\n+                            length = [15]\n+\n+        case .discover:     prefix = [\"6011\", \"65\"]\n+                            length = [16]\n+\n+        case .diners:       prefix = [\"300\"...\"305\", \"309\", \"38\"...\"39\"]\n+                            length = [14]\n+\n+        case .jcb:          prefix = [\"3528\"...\"3589\"]\n+                            length = [16]\n+        }\n+\n+        var card = ValidationRequirement()\n+        card.prefixes = prefix\n+        card.lengths = length\n+        return card\n+    }\n+\n+    var segmentGroupings: [Int] {\n+        switch self {\n+        case .amex:      return [4, 6, 5]\n+        case .diners:    return [4, 6, 4]\n+        default:         return [4, 4, 4, 4]\n+        }\n+    }\n+\n+    var maxLength: Int {\n+        return validationRequirements.lengths.max() ?? 16\n+    }\n+\n+    var cvvLength: Int {\n+        switch self {\n+        case .amex: return 4\n+        default: return 3\n+        }\n+    }\n+\n+    func valid(_ accountNumber: String) -> Bool {\n+        return validationRequirements.valid(accountNumber) && CardType.luhnCheck(accountNumber)\n+    }\n+\n+    func prefixValid(_ accountNumber: String) -> Bool {\n+        return validationRequirements.prefixValid(accountNumber)\n+    }\n+\n+}\n+\n+fileprivate extension CardType {\n+\n+    struct ValidationRequirement {\n+        var prefixes = [PrefixContainable]()\n+        var lengths = [Int]()\n+\n+        func valid(_ accountNumber: String) -> Bool {\n+            return lengthValid(accountNumber) && prefixValid(accountNumber)\n+        }\n+\n+        func prefixValid(_ accountNumber: String) -> Bool {\n+            guard prefixes.count > 0 else { return true }\n+            return prefixes.contains { $0.prefixMatches(accountNumber) }\n+        }\n+\n+        func lengthValid(_ accountNumber: String) -> Bool {\n+            guard lengths.count > 0 else { return true }\n+            return lengths.contains { accountNumber.characters.count == $0 }\n+        }\n+    }\n+\n+    // from: https://gist.github.com/cwagdev/635ce973e8e86da0403a\n+    static func luhnCheck(_ cardNumber: String) -> Bool {\n+        var sum = 0\n+        let reversedCharacters = cardNumber.characters.reversed().map { String($0) }\n+        for (idx, element) in reversedCharacters.enumerated() {\n+            guard let digit = Int(element) else { return false }\n+            switch ((idx % 2 == 1), digit) {\n+            case (true, 9): sum += 9\n+            case (true, 0...8): sum += (digit * 2) % 9\n+            default: sum += digit\n+            }\n+        }\n+        return sum % 10 == 0\n+    }\n+\n+}\n+\n+//MARK: - CardState\n+\n+enum CardState {\n+    case identified(CardType)\n+    case indeterminate([CardType])\n+    case invalid\n+}\n+\n+extension CardState: Equatable {}\n+func ==(lhs: CardState, rhs: CardState) -> Bool {\n+    switch (lhs, rhs) {\n+    case (.invalid, .invalid): return true\n+    case (.indeterminate, .indeterminate): return true\n+    case (let .identified(card1), let .identified(card2)): return card1 == card2\n+    default: return false\n+    }\n+}\n+\n+extension CardState {\n+\n+    static func fromNumber(_ cardNumber: String) -> CardState {\n+        for cardType in CardType.allValues {\n+            if cardType.valid(cardNumber) {\n+                return .identified(cardType)\n+            }\n+        }\n+        return .invalid\n+    }\n+\n+    static func fromPrefix(_ cardPrefix: String) -> CardState {\n+        guard !cardPrefix.isEmpty else {\n+            return .indeterminate(CardType.allValues)\n+        }\n+        \n+        let possibleTypes = CardType.allValues.filter { $0.prefixValid(cardPrefix) }\n+        guard let card = possibleTypes.first else {\n+            return .invalid\n+        }\n+        if possibleTypes.count == 1 {\n+            return .identified(card)\n+        }\n+        else {\n+            return .indeterminate(possibleTypes)\n+        }\n+    }\n+}\n+\n+//MARK: - PrefixContainable\n+\n+fileprivate protocol PrefixContainable {\n+\n+    func prefixMatches(_ text: String) -> Bool\n+    \n+}\n+\n+extension ClosedRange: PrefixContainable {\n+\n+    func prefixMatches(_ text: String) -> Bool {\n+\n+        //cannot include Where clause in protocol conformance, so have to ensure Bound == String :(\n+        guard !text.isEmpty, let lower = lowerBound as? String, let upper = upperBound as? String else { return false }\n+\n+        let trimmedRange: ClosedRange<String> = {\n+            let length = text.characters.count\n+            let trimmedStart = String(lower.characters.prefix(length))\n+            let trimmedEnd = String(upper.characters.prefix(length))\n+            return trimmedStart...trimmedEnd\n+        }()\n+\n+        let trimmedText = String(text.characters.prefix(trimmedRange.lowerBound.characters.count))\n+        return trimmedRange ~= trimmedText\n+    }\n+\n+}\n+\n+extension String: PrefixContainable {\n+\n+    func prefixMatches(_ text: String) -> Bool {\n+        return hasPrefix(text) || text.hasPrefix(self)","path":"RZCardEntry/RZCardEntry/CardType.swift","position":199,"original_position":199,"commit_id":"e7d7499ce8543231cb66a0007350fc9b8d0c230a","original_commit_id":"e7d7499ce8543231cb66a0007350fc9b8d0c230a","user":{"login":"jkaufman","id":57186,"avatar_url":"https://avatars.githubusercontent.com/u/57186?v=3","gravatar_id":"","url":"https://api.github.com/users/jkaufman","html_url":"https://github.com/jkaufman","followers_url":"https://api.github.com/users/jkaufman/followers","following_url":"https://api.github.com/users/jkaufman/following{/other_user}","gists_url":"https://api.github.com/users/jkaufman/gists{/gist_id}","starred_url":"https://api.github.com/users/jkaufman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jkaufman/subscriptions","organizations_url":"https://api.github.com/users/jkaufman/orgs","repos_url":"https://api.github.com/users/jkaufman/repos","events_url":"https://api.github.com/users/jkaufman/events{/privacy}","received_events_url":"https://api.github.com/users/jkaufman/received_events","type":"User","site_admin":false},"body":"#? Why are we checking both cases, here?  I don't follow why `\"AA\".hasPrefix(\"AAB\")` should return `true`.","created_at":"2016-11-14T17:30:17Z","updated_at":"2016-11-14T17:30:51Z","html_url":"https://github.com/heyltsjay/RZCardEntry/pull/2#discussion_r87850297","pull_request_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2","_links":{"self":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/comments/87850297"},"html":{"href":"https://github.com/heyltsjay/RZCardEntry/pull/2#discussion_r87850297"},"pull_request":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2"}}},"pull_request":{"url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2","id":93582182,"html_url":"https://github.com/heyltsjay/RZCardEntry/pull/2","diff_url":"https://github.com/heyltsjay/RZCardEntry/pull/2.diff","patch_url":"https://github.com/heyltsjay/RZCardEntry/pull/2.patch","issue_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/2","number":2,"state":"open","locked":false,"title":"CardType","user":{"login":"heyltsjay","id":18120479,"avatar_url":"https://avatars.githubusercontent.com/u/18120479?v=3","gravatar_id":"","url":"https://api.github.com/users/heyltsjay","html_url":"https://github.com/heyltsjay","followers_url":"https://api.github.com/users/heyltsjay/followers","following_url":"https://api.github.com/users/heyltsjay/following{/other_user}","gists_url":"https://api.github.com/users/heyltsjay/gists{/gist_id}","starred_url":"https://api.github.com/users/heyltsjay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heyltsjay/subscriptions","organizations_url":"https://api.github.com/users/heyltsjay/orgs","repos_url":"https://api.github.com/users/heyltsjay/repos","events_url":"https://api.github.com/users/heyltsjay/events{/privacy}","received_events_url":"https://api.github.com/users/heyltsjay/received_events","type":"User","site_admin":false},"body":"","created_at":"2016-11-14T15:03:32Z","updated_at":"2016-11-14T17:30:51Z","closed_at":null,"merged_at":null,"merge_commit_sha":"41ce869c7c97c0e7239d74b8723f03f71509921b","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2/commits","review_comments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2/comments","review_comment_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/comments{/number}","comments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/2/comments","statuses_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/statuses/e7d7499ce8543231cb66a0007350fc9b8d0c230a","head":{"label":"heyltsjay:CardTypeFeedbackPR","ref":"CardTypeFeedbackPR","sha":"e7d7499ce8543231cb66a0007350fc9b8d0c230a","user":{"login":"heyltsjay","id":18120479,"avatar_url":"https://avatars.githubusercontent.com/u/18120479?v=3","gravatar_id":"","url":"https://api.github.com/users/heyltsjay","html_url":"https://github.com/heyltsjay","followers_url":"https://api.github.com/users/heyltsjay/followers","following_url":"https://api.github.com/users/heyltsjay/following{/other_user}","gists_url":"https://api.github.com/users/heyltsjay/gists{/gist_id}","starred_url":"https://api.github.com/users/heyltsjay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heyltsjay/subscriptions","organizations_url":"https://api.github.com/users/heyltsjay/orgs","repos_url":"https://api.github.com/users/heyltsjay/repos","events_url":"https://api.github.com/users/heyltsjay/events{/privacy}","received_events_url":"https://api.github.com/users/heyltsjay/received_events","type":"User","site_admin":false},"repo":{"id":62075790,"name":"RZCardEntry","full_name":"heyltsjay/RZCardEntry","owner":{"login":"heyltsjay","id":18120479,"avatar_url":"https://avatars.githubusercontent.com/u/18120479?v=3","gravatar_id":"","url":"https://api.github.com/users/heyltsjay","html_url":"https://github.com/heyltsjay","followers_url":"https://api.github.com/users/heyltsjay/followers","following_url":"https://api.github.com/users/heyltsjay/following{/other_user}","gists_url":"https://api.github.com/users/heyltsjay/gists{/gist_id}","starred_url":"https://api.github.com/users/heyltsjay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heyltsjay/subscriptions","organizations_url":"https://api.github.com/users/heyltsjay/orgs","repos_url":"https://api.github.com/users/heyltsjay/repos","events_url":"https://api.github.com/users/heyltsjay/events{/privacy}","received_events_url":"https://api.github.com/users/heyltsjay/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/heyltsjay/RZCardEntry","description":"Credit card entry for swift","fork":false,"url":"https://api.github.com/repos/heyltsjay/RZCardEntry","forks_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/forks","keys_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/keys{/key_id}","collaborators_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/teams","hooks_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/hooks","issue_events_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/events{/number}","events_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/events","assignees_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/assignees{/user}","branches_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/branches{/branch}","tags_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/tags","blobs_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/refs{/sha}","trees_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/trees{/sha}","statuses_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/statuses/{sha}","languages_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/languages","stargazers_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/stargazers","contributors_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/contributors","subscribers_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/subscribers","subscription_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/subscription","commits_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/commits{/sha}","git_commits_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/commits{/sha}","comments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/comments{/number}","issue_comment_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/comments{/number}","contents_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/contents/{+path}","compare_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/compare/{base}...{head}","merges_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/merges","archive_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/downloads","issues_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues{/number}","pulls_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls{/number}","milestones_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/milestones{/number}","notifications_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/labels{/name}","releases_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/releases{/id}","deployments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/deployments","created_at":"2016-06-27T17:47:26Z","updated_at":"2016-10-31T16:25:20Z","pushed_at":"2016-11-14T15:03:32Z","git_url":"git://github.com/heyltsjay/RZCardEntry.git","ssh_url":"git@github.com:heyltsjay/RZCardEntry.git","clone_url":"https://github.com/heyltsjay/RZCardEntry.git","svn_url":"https://github.com/heyltsjay/RZCardEntry","homepage":"","size":279,"stargazers_count":0,"watchers_count":0,"language":"Swift","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"develop"}},"base":{"label":"heyltsjay:CardTypeFeedback","ref":"CardTypeFeedback","sha":"3f81aae0c81817de33fd9b0de664406054b6dc8e","user":{"login":"heyltsjay","id":18120479,"avatar_url":"https://avatars.githubusercontent.com/u/18120479?v=3","gravatar_id":"","url":"https://api.github.com/users/heyltsjay","html_url":"https://github.com/heyltsjay","followers_url":"https://api.github.com/users/heyltsjay/followers","following_url":"https://api.github.com/users/heyltsjay/following{/other_user}","gists_url":"https://api.github.com/users/heyltsjay/gists{/gist_id}","starred_url":"https://api.github.com/users/heyltsjay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heyltsjay/subscriptions","organizations_url":"https://api.github.com/users/heyltsjay/orgs","repos_url":"https://api.github.com/users/heyltsjay/repos","events_url":"https://api.github.com/users/heyltsjay/events{/privacy}","received_events_url":"https://api.github.com/users/heyltsjay/received_events","type":"User","site_admin":false},"repo":{"id":62075790,"name":"RZCardEntry","full_name":"heyltsjay/RZCardEntry","owner":{"login":"heyltsjay","id":18120479,"avatar_url":"https://avatars.githubusercontent.com/u/18120479?v=3","gravatar_id":"","url":"https://api.github.com/users/heyltsjay","html_url":"https://github.com/heyltsjay","followers_url":"https://api.github.com/users/heyltsjay/followers","following_url":"https://api.github.com/users/heyltsjay/following{/other_user}","gists_url":"https://api.github.com/users/heyltsjay/gists{/gist_id}","starred_url":"https://api.github.com/users/heyltsjay/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/heyltsjay/subscriptions","organizations_url":"https://api.github.com/users/heyltsjay/orgs","repos_url":"https://api.github.com/users/heyltsjay/repos","events_url":"https://api.github.com/users/heyltsjay/events{/privacy}","received_events_url":"https://api.github.com/users/heyltsjay/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/heyltsjay/RZCardEntry","description":"Credit card entry for swift","fork":false,"url":"https://api.github.com/repos/heyltsjay/RZCardEntry","forks_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/forks","keys_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/keys{/key_id}","collaborators_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/teams","hooks_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/hooks","issue_events_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/events{/number}","events_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/events","assignees_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/assignees{/user}","branches_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/branches{/branch}","tags_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/tags","blobs_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/refs{/sha}","trees_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/trees{/sha}","statuses_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/statuses/{sha}","languages_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/languages","stargazers_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/stargazers","contributors_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/contributors","subscribers_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/subscribers","subscription_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/subscription","commits_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/commits{/sha}","git_commits_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/git/commits{/sha}","comments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/comments{/number}","issue_comment_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/comments{/number}","contents_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/contents/{+path}","compare_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/compare/{base}...{head}","merges_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/merges","archive_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/downloads","issues_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues{/number}","pulls_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls{/number}","milestones_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/milestones{/number}","notifications_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/labels{/name}","releases_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/releases{/id}","deployments_url":"https://api.github.com/repos/heyltsjay/RZCardEntry/deployments","created_at":"2016-06-27T17:47:26Z","updated_at":"2016-10-31T16:25:20Z","pushed_at":"2016-11-14T15:03:32Z","git_url":"git://github.com/heyltsjay/RZCardEntry.git","ssh_url":"git@github.com:heyltsjay/RZCardEntry.git","clone_url":"https://github.com/heyltsjay/RZCardEntry.git","svn_url":"https://github.com/heyltsjay/RZCardEntry","homepage":"","size":279,"stargazers_count":0,"watchers_count":0,"language":"Swift","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":1,"forks":0,"open_issues":1,"watchers":0,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2"},"html":{"href":"https://github.com/heyltsjay/RZCardEntry/pull/2"},"issue":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/2"},"comments":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/issues/2/comments"},"review_comments":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2/comments"},"review_comment":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/pulls/2/commits"},"statuses":{"href":"https://api.github.com/repos/heyltsjay/RZCardEntry/statuses/e7d7499ce8543231cb66a0007350fc9b8d0c230a"}}}}
{"actor":{"display_login":"jkaufman"}}