// Learn more about F# at http://fsharp.net
// See the 'F# Tutorial' project for more help.

type Cond = char * bool

type P = Cond List * double

type Variable =
    | OneD of Cond * Cond * double
    | TwoD of Cond * Cond * Cond * double
    | Prior of Cond * double

let getProbability(p) =
    match p with
    | OneD(_,_,prob) -> prob
    | TwoD(_,_,_,prob) -> prob
    | Prior(_,prob) -> prob

let filterCondition (c, b) v =
    match v with
    | OneD((pc,pb),_,_) when c = pc && b = pb  -> true
    | OneD(_,(pc,pb),_) when c = pc && b = pb -> true
    | Prior((pc,pb),_) when c = pc && b = pb -> true 
    | TwoD((pc,pb),_,_,_) when c = pc && b = pb -> true
    | TwoD(_,(pc,pb),_,_) when c = pc && b = pb -> true
    | TwoD(_,_,(pc,pb),_) when c = pc && b = pb -> true
    | _ -> false

let reciprocal v =
    match v with
    | Prior((pc,pb),p) -> Prior((pc,not pb),1.0-p)
    | OneD((pc,pb),a,p) -> OneD((pc,not pb),a,1.0-p)
    | TwoD((pc,pb),a,b,p) -> TwoD((pc,not pb),a,b,1.0-p)

let matchProb cond variables =  cond |> List.fold (fun s c -> List.filter (filterCondition c) s) variables |> List.head |> getProbability

let completeReciprocalVariable variable = variable |> List.map reciprocal |> List.append variable

//let addNeg ps = ps concat ps

//let pa = [Prior(('a', true), 0.01)]
//
//let pta = [OneD(('t', true), ('a', true), 0.5);
//            OneD(('t', true), ('a', false), 0.1)]
//
//let ps = [Prior(('s', true), 0.5)]
//
//let pls = [OneD(('l', true), ('s', true), 0.1);
//            OneD(('l', true), ('s', false), 0.01)]
//
//let pbs = [OneD(('b', true), ('s', true), 0.6);
//            OneD(('b', true), ('s', false), 0.3)]

let pm = [Prior(('m',true), 0.1)] |> completeReciprocalVariable
let pbm = [OneD(('b',true), ('m',true), 0.9); OneD(('b',true), ('m',false), 0.05)] |> completeReciprocalVariable
let psm = [OneD(('s',true), ('m',true), 0.6); OneD(('s',true), ('m',false), 0.3)] |> completeReciprocalVariable

let pbym = (pm |> matchProb [('m', true)]) * (pbm |> matchProb [('b',true);('m',true)]) , (pm |> matchProb [('m', false)]) * (pbm |> matchProb [('b',true);('m',false)])
let pbysym = (pm |> matchProb [('m', true)]) * (pbm |> matchProb [('b',true);('m',true)]) * (psm |> matchProb [('s',true);('m',true)]),
                (pm |> matchProb [('m', false)]) * (pbm |> matchProb [('b',true);('m',false)]) * (psm |> matchProb [('s',true);('m',false)])




let ps = [Prior(('s',true), 0.3)] |> completeReciprocalVariable
let pr = [Prior(('r',true), 0.2)] |> completeReciprocalVariable
let pwsr = [TwoD(('w',true), ('s',true), ('r',true), 0.9);  TwoD(('w',true), ('s',true), ('r',false), 0.7);
            TwoD(('w',true), ('s',false), ('r',true), 0.8); TwoD(('w',true), ('s',false), ('r',false), 0.1); ] |> completeReciprocalVariable

let pwyr = (pr |> matchProb [('r', true)]) *
           ((pwsr |> matchProb [('r',true);('w',true);('s',true)]) * (ps |> matchProb [('s',true)]) + 
            (pwsr |> matchProb [('r',true);('w',true);('s',false)]) * (ps |> matchProb [('s',false)]))


[<EntryPoint>]
let main argv = 

    printfn "%A" pwyr
    0 // return an integer exit code
