anchor.diff:  // Super simple model: predict based on one single token, "txt"
   let txtClassifier = train training wordTokenizer (["txt"] |> set)
   
  -validation 
  -|> Seq.averageBy (fun (l,o) -> 
  +validation
  +|> Seq.averageBy (fun (l,o) ->
       if l = txtClassifier o then 1. else 0.)
   |> printfn "Based on 'txt', correctly classified: %.3f"
   
   // Even simpler model: always predict the most frequent class,
   // in that case, Ham
  -validation 
  -|> Seq.averageBy (fun (l,o) -> 
  +validation
  +|> Seq.averageBy (fun (l,o) ->
       if l = Ham then 1. else 0.)
   |> printfn "Aways Ham, correctly classified: %.3f"
   
   // Use every single token
  -let tokenized = 
  -    training 
  +let tokenized =
  +    training
       |> Seq.map (fun (lbl,sms) -> lbl,wordTokenizer sms)
   
  -let allTokens = 
  +let allTokens =
       training
       |> Seq.map snd
       |> vocabulary wordTokenizer
   
   let fullClassifier = train training wordTokenizer allTokens
  -validation 
  -|> Seq.averageBy (fun (l,o) -> 
  +validation
  +|> Seq.averageBy (fun (l,o) ->
       if l = fullClassifier o then 1. else 0.)
   |> printfn "Based on all tokens, correctly classified: %.3f"
   
   // generic model evaluation function
  -let evaluate (tokenizer:Tokenizer) (tokens:Token Set) = 
  +let evaluate (tokenizer:Tokenizer) (tokens:Token Set) =
       let model = train training tokenizer tokens
  -    validation 
  -    |> Seq.averageBy (fun (l,o) -> 
  +    validation
  +    |> Seq.averageBy (fun (l,o) ->
           if l = model o then 1. else 0.)
       |> printfn "Correctly classified: %.3f"
   
   evaluate wordTokenizer allTokens
   
   // does casing matter?
  -let casedTokenizer (text:string) = 
  +let casedTokenizer (text:string) =
       text
       |> matchWords.Matches
       |> Seq.cast<Match>
       |> Seq.map (fun m -> m.Value)
       |> Set.ofSeq
   
  -let casedTokens = 
  -    training 
  -    |> Seq.map snd 
  +let casedTokens =
  +    training
  +    |> Seq.map snd
       |> vocabulary casedTokenizer
   
   evaluate casedTokenizer casedTokens
   
   // most frequent tokens
   
  -let top n (tokenizer:Tokenizer) (docs:string []) = 
  +let top n (tokenizer:Tokenizer) (docs:string []) =
       let tokenized = docs |> Array.map tokenizer
       let tokens = tokenized |> Set.unionMany
  -    tokens 
  +    tokens
       |> Seq.sortBy (fun t -> - countIn tokenized t)
       |> Seq.take n
       |> Set.ofSeq
   
  -let ham,spam = 
  +let ham,spam =
       let rawHam,rawSpam =
  -        training 
  +        training
           |> Array.partition (fun (lbl,_) -> lbl=Ham)
  -    rawHam |> Array.map snd, 
  +    rawHam |> Array.map snd,
       rawSpam |> Array.map snd
   
   let hamCount = ham |> vocabulary casedTokenizer |> Set.count
   let spamCount = spam |> vocabulary casedTokenizer |> Set.count
   
  -let topHam = ham |> top (hamCount / 10) casedTokenizer 
  -let topSpam = spam |> top (spamCount / 10) casedTokenizer 
  +let topHam = ham |> top (hamCount / 10) casedTokenizer
  +let topSpam = spam |> top (spamCount / 10) casedTokenizer
   
   let topTokens = Set.union topHam topSpam
   
  
positive.code: 
  smartTokenizer "hello World, call 08123456789 or txt 12345"
  
  let smartTokens = 
      specificTokens 
      |> Set.add "__TXT__" 
      |> Set.add "__PHONE__"  
  
  evaluate smartTokenizer smartTokens
  
  
negative.code: 
  // Define the 2 types of Document
  // using a Discriminated Union.
  type DocType =
      | Ham
      | Spam
  
  