<commit_before>//
//  LineageAlignment.swift
//  TaxonomyKit
//
//  Created by <NAME> on 7/6/17.
//  Copyright © 2017 <NAME>. All rights reserved.
//

import Foundation

public class LineageAlignment {
    
    public class Cell {
        public internal(set) var offset: Int = 0
        public private(set) var node: LineageTree.Node
        
        init(node: LineageTree.Node) {
            self.node = node
        }
    
    }
    
    public class Column: CustomDebugStringConvertible {
        public var rank: TaxonomicRank? = nil
        public var cells: [Cell] = []
        init(rank: TaxonomicRank?) {
            self.rank = rank
        }
        
        public var debugDescription: String {
            return "[\(cells.count):\(rank?.rawValue ?? "no rank")]: \(cells.map{$0.node.name}.joined(separator:", "))"
        }
        
        public var span: Int {
            return cells.reduce(0) { $0 + $1.node.span }
        }
        
        public var count: Int {
            return cells.count
        }
        
        public func participatesInLineageOf(_ endPoint: LineageTree.Node) -> Bool {
            var testNode: LineageTree.Node? = endPoint
            while testNode != nil {
                for cell in cells {
                    if testNode === cell.node {
                        return true
                    }
                }
                testNode = testNode?.parent
            }
            return false
        }
        
        public subscript(index: Int) -> Cell {
            return cells[index]
        }
        
    }
    
    public private(set) var grid: [Column] = TaxonomicRank.hierarchy.map { Column(rank: $0) }
    
    private func currentDepth(for rank: TaxonomicRank) -> Int {
        var i = 0
        for pair in grid {
            if pair.rank == rank {
                return i
            }
            i += 1
        }
        return -1
    }
    
    public var cleanedUp: [Column] {
        return grid.filter{ $0.cells.count > 0 }
    }
    
    public init(lineageTree: LineageTree) {
        let root = lineageTree.rootNode
        parseNode(root, depth: 0)
        
        let endPoints = lineageTree.endPoints.sorted{ $0.sortString < $1.sortString }
        for column in grid {
            for (x, cell) in column.cells.enumerated() {
                guard let _ = cell.node.parent else {
                    cell.offset = 0
                    continue
                }
                var offset = x
                for endPoint in endPoints {
                    if endPoint === cell.node {
                        break
                    }
                    
                    if !column.participatesInLineageOf(endPoint) {
                        offset += 1
                    } else if !cell.node.isPresentInLineageOf(endPoint) {
                        continue
                    } else {
                        cell.offset = offset
                        break
                    }
                }
            }
        }
    }
    
    private func parseNode(_ node: LineageTree.Node, depth: Int) {
        // Check if node has rank
        if let rank = node.rank {
            // Node has rank
            let currentDepth = self.currentDepth(for: rank)
            if currentDepth == depth {
                grid[depth].cells.append(Cell(node: node))
                let sortedChildren = node.children.sorted { $0.sortString < $1.sortString }
                for child in sortedChildren {
                    parseNode(child, depth: depth + 1)
                }
            } else if currentDepth < depth {
                let emptyPair = Column(rank: nil)
                grid.insert(emptyPair, at: depth)
                parseNode(node, depth: depth)
            } else {
                parseNode(node, depth: currentDepth)
            }
        } else {
            var extraIterations = 1<commit_msg>Fixed offsets<commit_after>//
//  LineageAlignment.swift
//  TaxonomyKit
//
//  Created by <NAME> on 7/6/17.
//  Copyright © 2017 <NAME>. All rights reserved.
//

import Foundation

public class LineageAlignment {
    
    public class Cell {
        public internal(set) var offset: Int = -1
        public private(set) var node: LineageTree.Node
        
        init(node: LineageTree.Node) {
            self.node = node
        }
    
    }
    
    public class Column: CustomDebugStringConvertible {
        public var rank: TaxonomicRank? = nil
        public var cells: [Cell] = []
        init(rank: TaxonomicRank?) {
            self.rank = rank
        }
        
        public var debugDescription: String {
            return "[\(cells.count):\(rank?.rawValue ?? "no rank")]: \(cells.map{$0.node.name}.joined(separator:", "))"
        }
        
        public var span: Int {
            return cells.reduce(0) { $0 + $1.node.span }
        }
        
        public var count: Int {
            return cells.count
        }
        
        public func participatesInLineageOf(_ endPoint: LineageTree.Node) -> Bool {
            var testNode: LineageTree.Node? = endPoint
            while testNode != nil {
                for cell in cells {
                    if testNode === cell.node {
                        return true
                    }
                }
                testNode = testNode?.parent
            }
            return false
        }
        
        public subscript(index: Int) -> Cell {
            return cells[index]
        }
        
    }
    
    public private(set) var grid: [Column] = TaxonomicRank.hierarchy.map { Column(rank: $0) }
    
    private func currentDepth(for rank: TaxonomicRank) -> Int {
        var i = 0
        for pair in grid {
            if pair.rank == rank {
                return i
            }
            i += 1
        }
        return -1
    }
    
    public var cleanedUp: [Column] {
        return grid.filter{ $0.cells.count > 0 }
    }
    
    public init(lineageTree: LineageTree) {
        let root = lineageTree.rootNode
        parseNode(root, depth: 0)
        
        let endPoints = lineageTree.endPoints.sorted{ $0.sortString < $1.sortString }
        for column in grid {
            for (x, cell) in column.cells.enumerated() {
                guard let _ = cell.node.parent else {
                    cell.offset = 0
                    continue
                }
                var offset = x
                for endPoint in endPoints {
                    if endPoint === cell.node {
                        cell.offset = offset
                        break
                    }
                    if !column.participatesInLineageOf(endPoint) {
                        offset += 1
                    } else if !cell.node.isPresentInLineageOf(endPoint) {
                        continue
                    } else {
                        cell.offset = offset
                        break
                    }
                }
                if cell.offset == -1 {
                    cell.offset = offset
                }
            }
        }
    }
    
    private func parseNode(_ node: LineageTree.Node, depth: Int) {
        // Check if node has rank
        if let rank = node.rank {
            // Node has rank
            let currentDepth = self.currentDepth(for: rank)
            if currentDepth == depth {
                grid[depth].cells.append(Cell(node: node))
                let sortedChildren = node.children.sorted { $0.sortString < $1.sortString }
                for child in sortedChildren {
                    parseNode(child, depth: depth + 1)
                }
            } else if currentDepth < depth {
                let emptyPair = Column(rank: nil)
                grid.insert(emptyPair, at: depth)
                parseNode(node, depth: depth)
            } else {
                parseNode(node, depth: currentDepth)
            }
        } else {
            var extraIterations = 1
{'fraction_non_alphanumeric': 0.08759582757320598, 'fraction_numerical': 0.0071635038331029285, 'mean_word_length': 1.1695747001090513, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2743895', 'n_tokens_mistral': 2143, 'n_tokens_neox': 2005, 'n_words': 535}