[SodiumFRP + F#] 組合せ論理回路のモデル化

# 概要
組合せ論理回路を関数型リアクティブプログラミング（SodiumFRP）の世界でモデル化してみました。

## 合成性

XOR（排他的論理和）は、数式／MIL記号回路では、次のように表現することができます。

```math
X = (\overline{A \cdot B}) \cdot (A + B)
```

![circuit.png](https://qiita-image-store.s3.amazonaws.com/0/152805/fa431fbf-d13f-3b0f-c2f1-efed6b6effa1.png)

これを、F#+SodiumFRP で、次のようにコードとして表現できるようにしてみました。自然な形で合成していくことができます。
 
```FSharp:XOR
let inA = StreamSink.create<Option<bool>> ()
let inB = StreamSink.create<Option<bool>> ()
let outP = (inA, inB) |> AND |> NOT
let outQ = (inA, inB) |> OR
let outX = (outP, outQ) |> AND
```

# コード

```FSharp
open System
open SodiumFRP

let eNOT (a:Option<bool>) =
  match a.IsSome with
  | true -> Some(not a.Value)
  | false -> None

let eAND (a:Option<bool>) (b:Option<bool>) =
  match (a.IsSome,b.IsSome) with
  | (true ,true ) -> Some ( a.Value && b.Value )
  | (true ,false) -> if a.Value = false then Some false else None
  | (false,true ) -> if b.Value = false then Some false else None
  | (false,false) -> None

let eOR (a:Option<bool>) (b:Option<bool>) =
  match (a.IsSome,b.IsSome) with
  | (true ,true ) -> Some ( a.Value || b.Value )
  | (true ,false) -> if a.Value = true then Some true else None
  | (false,true ) -> if b.Value = true then Some true else None
  | (false,false) -> None

let eXOR (a:Option<bool>) (b:Option<bool>) =
  match (a.IsSome,b.IsSome) with
  | (true ,true) -> if a.Value = b.Value then Some false else Some true
  | _ -> None

let eNAND a b = eAND a b |> eNOT
let eNOR a b = eOR a b |> eNOT

let NOT a = mapS eNOT a
let AND (a,b) = mergeS eAND (a,b)
let OR (a,b) = mergeS eOR (a,b)
let XOR (a,b) = mergeS eXOR (a,b)

let str (x:Option<bool>) = 
  match x.IsSome with
  | true -> if x.Value then "1" else "0"
  | false -> "*"

let twoInputList = [ 
  (Some false, Some false)
  (Some false, Some true)
  (Some true, Some false)
  (Some true, Some true)
  (None, Some true)
  (None, Some false)
  (Some true, None)
  (Some false, None)
  (None, None)
]

let run21 (i1,i2) o1 (d1,d2) = 
  let i1' = i1 |> holdS None
  let i2' = i2 |> holdS None
  let o1' = o1 |> holdS None
  Transaction.run( fun () -> 
    i1 |> sendS d1
    i2 |> sendS d2
  ) 
  ((sampleC i1', sampleC i2'), sampleC o1')

let run22 (i1,i2) (o1,o2) (d1,d2) = 
  let i1' = i1 |> holdS None
  let i2' = i2 |> holdS None
  let o1' = o1 |> holdS None
  let o2' = o2 |> holdS None
  Transaction.run( fun () -> 
    i1 |> sendS d1
    i2 |> sendS d2
  ) 
  ((sampleC i1', sampleC i2'), (sampleC o1',sampleC o2'))


let logicTest21 (i1,i2) o1 =
  let rec testAll testPatterns =
    match testPatterns with 
    | x::xs-> (run21 (i1,i2) o1 x)::(testAll xs)
    | [] -> []
  testAll twoInputList

let logicTest22 (i1,i2) (o1,o2) =
  let rec testAll testPatterns =
    match testPatterns with 
    | x::xs-> (run22 (i1,i2) (o1,o2) x)::(testAll xs)
    | [] -> []
  testAll twoInputList

let printTestResult21 data =
  let print ((i1,i2), o1) =
    let s = sprintf " %s %s | %s" (str i1) (str i2) (str o1)
    do Console.WriteLine(s)
  do Console.WriteLine(" A B | X")
  for i in data do
    do print <| i
 
let printTestResult22 data =
  let print ((i1,i2), (o1,o2)) =
    let s = sprintf " %s %s | %s %s" (str i1) (str i2) (str o1) (str o2)
    do Console.WriteLine(s)
  do Console.WriteLine(" A B | X Y")
  for i in data do
    do print <| i

let NL = System.Environment.NewLine

[<EntryPoint>]
let main argv =

  let inA = StreamSink.create<Option<bool>> ()
  let inB = StreamSink.create<Option<bool>> ()

  do Console.WriteLine("AND（論理積）")
  let outX = (inA, inB) |> AND
  let rAND =  logicTest21 (inA,inB) outX 
  do printTestResult21 rAND

  do Console.WriteLine(NL+"OR（論理和）")
  let outX = (inA, inB) |> OR
  let rOR = logicTest21 (inA,inB) outX 
  do printTestResult21 rOR

  do Console.WriteLine(NL+"XOR -> rXOR_1")
  let outX = (inA, inB) |> XOR
  let rXOR_1 = logicTest21 (inA,inB) outX
  do printTestResult21 rXOR_1

  do Console.WriteLine(NL+"XOR(合成版) -> rXOR_2")
  let outP = (inA, inB) |> AND |> NOT
  let outQ = (inA, inB) |> OR
  let outX = (outP, outQ ) |> AND
  let rXOR_2 = logicTest21 (inA,inB) outX
  do printTestResult21 rXOR_2

  do Console.Write(NL+"rXOR_1 と rXOR_2 は等しい? ")
  let a = ( rXOR_1 = rXOR_2 )
  Console.WriteLine(a)

  do Console.WriteLine(NL+"NANDとNOR")
  let outX = (inA, inB) |> AND |> NOT
  let outY = (inA, inB) |> OR |> NOT
  let r = logicTest22 (inA,inB) (outX,outY) 
  do printTestResult22 r

  do Console.ReadKey() |> ignore

  0
```

# 実行結果

```

AND（論理積）
 A B | X
 0 0 | 0
 0 1 | 0
 1 0 | 0
 1 1 | 1
 * 1 | *
 * 0 | 0
 1 * | *
 0 * | 0
 * * | *

OR（論理和）
 A B | X
 0 0 | 0
 0 1 | 1
 1 0 | 1
 1 1 | 1
 * 1 | 1
 * 0 | *
 1 * | 1
 0 * | *
 * * | *

XOR -> rXOR_1
 A B | X
 0 0 | 0
 0 1 | 1
 1 0 | 1
 1 1 | 0
 * 1 | *
 * 0 | *
 1 * | *
 0 * | *
 * * | *

XOR(合成版) -> rXOR_2
 A B | X
 0 0 | 0
 0 1 | 1
 1 0 | 1
 1 1 | 0
 * 1 | *
 * 0 | *
 1 * | *
 0 * | *
 * * | *

rXOR_1 と rXOR_2 は等しい? True

NANDとNOR
 A B | X Y
 0 0 | 1 1
 0 1 | 1 0
 1 0 | 1 0
 1 1 | 0 0
 * 1 | * 0
 * 0 | 1 *
 1 * | * 0
 0 * | 1 *
 * * | * *
```

F#では、関数のオーバーロードができず、、、引数違いの関数をうまくまとめられず不細工な感じになってしました。

次は順序回路に発展させていきたいと思います。