         }
     }
     
-    public struct IndexedFeature: Hashable {
-        let feature: Feature
-        let index: Int
-        
-        public var hashValue: Int { return index }
-    }
-    
     // MARK: - Datum
     
-    public struct Datum {
+    public struct Datum: Printable {
         public let features: [Feature]
         public let classification: String
         
         public init(features: [Feature], classification: String) {
             self.features = features
             self.classification = classification
+        }
+        
+        public var description: String {
+            let featuresStr = ",".join(features.map { $0.description })
+            return "[\(featuresStr)] -> \(classification)"
         }
     }
     

    if lhs.isNumeric != rhs.isNumeric { return false }
    return lhs.isNumeric ? lhs.number == rhs.number : lhs.category == rhs.category
}

public func ==(lhs: DecisionTree.IndexedFeature, rhs: DecisionTree.IndexedFeature) -> Bool {
    return lhs.index == rhs.index
}

        return (left, right)
    }
    
    static func probability(classification: String, _ data: [Datum]) -> Double {
        var count = 0
        for datum in data {
            if datum.classification == classification { count++ }
        }
        return Double(count) / Double(data.count)
    }
    
    static func entropy(data: [Datum]) -> Double {
        let classifications = uniqueClassifications(data)
        var sum = 0.0
        for classification in classifications {
            let p = probability(classification, data)
            sum += -p * log2(p)
        }
        return sum
