let inverseFizzBuzz input =
let lcmWord, lcmNumber = "fizzbuzz", 15
let partialResultMap =
[0 .. lcmNumber] |> List.choose (fun n ->
match n % 3, n % 5 with
| 0, 0 -> Some("fizzbuzz", n)
| 0, _ -> Some("fizz", n)
| _, 0 -> Some("buzz", n)
| _ -> None)
|> fun ls -> [1 .. ls.Length] |> Seq.collect (fun n -> Seq.windowed n ls)
|> Seq.map (Array.unzip >> fun (f, s) -> List.ofArray f, [|s.[s.Length - 1]; s.[0]|])
|> Seq.sortBy (snd >> Array.reduce (-))
|> Seq.distinctBy fst
|> Map.ofSeq
|> Map.add [lcmWord] [|lcmNumber; lcmNumber|]
let getPartialResult start words =
Map.tryFind words partialResultMap |> Option.map (Array.map ((+) start))
(Array.ofSeq input, []) |> Seq.unfold (function
| [||], _ -> None
| words, first ->
match Array.tryFindIndex ((=) lcmWord) words with
| Some i -> Some(first @ List.ofArray words.[.. i], (words.[i + 1 ..], [lcmWord]))
| None -> Some(first @ List.ofArray words, ([||], [lcmWord])))
|> Seq.toArray
|> Array.mapi ((*) lcmNumber >> getPartialResult)
|> function
| xs when Array.forall Option.isSome xs -> Some(xs.[0].Value.[1], xs.[xs.Length - 1].Value.[0])
| _ -> None
[
["fizz"]
["buzz"]
["fizzbuzz"]
["fizz"; "buzz"]
["buzz"; "fizz"]
["fizz"; "buzz"; "fizz"]
["fizz"; "fizz"]
["fizz"; "fizz"; "buzz"]
["buzz"; "buzz"]
["fizz"; "fizzbuzz"]
["fizzbuzz"; "fizz"]
["fizz"; "fizzbuzz"; "fizz"]
["fizzbuzz"; "fizz"; "buzz"; "fizz"; "fizz"; "buzz"; "fizz"; "fizzbuzz"]
["fizz"; "fizzbuzz"; "fizz"; "buzz"; "fizz"; "fizz"; "buzz"; "fizz"; "fizzbuzz"; "fizz"; "buzz"; "fizz"; "fizz"; "buzz"; "fizz"; "fizzbuzz"; "fizz"; "buzz"]
[""]
["abc"]
] |> List.map inverseFizzBuzz |> List.zip [
Some(3, 3)
Some(5, 5)
Some(15, 15)
Some(9, 10)
Some(5, 6)
Some(3, 6)
Some(6, 9)
Some(6, 10)
None
Some(12, 15)
Some(15, 18)
Some(12, 18)
Some(15, 30)
Some(12, 50)
None
None
] |> List.tryFind ((<||) (<>))
|> printfn "%A"
bGV0IGludmVyc2VGaXp6QnV6eiBpbnB1dCA9CiAgICBsZXQgbGNtV29yZCwgbGNtTnVtYmVyID0gImZpenpidXp6IiwgMTUKICAgIGxldCBwYXJ0aWFsUmVzdWx0TWFwID0KICAgICAgICBbMCAuLiBsY21OdW1iZXJdIHw+IExpc3QuY2hvb3NlIChmdW4gbiAtPgogICAgICAgICAgICBtYXRjaCBuICUgMywgbiAlIDUgd2l0aAogICAgICAgICAgICB8IDAsIDAgLT4gU29tZSgiZml6emJ1enoiLCBuKQogICAgICAgICAgICB8IDAsIF8gLT4gU29tZSgiZml6eiIsIG4pCiAgICAgICAgICAgIHwgXywgMCAtPiBTb21lKCJidXp6IiwgbikKICAgICAgICAgICAgfCBfIC0+IE5vbmUpCiAgICAgICAgfD4gZnVuIGxzIC0+IFsxIC4uIGxzLkxlbmd0aF0gfD4gU2VxLmNvbGxlY3QgKGZ1biBuIC0+IFNlcS53aW5kb3dlZCBuIGxzKQogICAgICAgIHw+IFNlcS5tYXAgKEFycmF5LnVuemlwID4+IGZ1biAoZiwgcykgLT4gTGlzdC5vZkFycmF5IGYsIFt8cy5bcy5MZW5ndGggLSAxXTsgcy5bMF18XSkKICAgICAgICB8PiBTZXEuc29ydEJ5IChzbmQgPj4gQXJyYXkucmVkdWNlICgtKSkKICAgICAgICB8PiBTZXEuZGlzdGluY3RCeSBmc3QKICAgICAgICB8PiBNYXAub2ZTZXEKICAgICAgICB8PiBNYXAuYWRkIFtsY21Xb3JkXSBbfGxjbU51bWJlcjsgbGNtTnVtYmVyfF0KCiAgICBsZXQgZ2V0UGFydGlhbFJlc3VsdCBzdGFydCB3b3JkcyA9CiAgICAgICAgTWFwLnRyeUZpbmQgd29yZHMgcGFydGlhbFJlc3VsdE1hcCB8PiBPcHRpb24ubWFwIChBcnJheS5tYXAgKCgrKSBzdGFydCkpCgogICAgKEFycmF5Lm9mU2VxIGlucHV0LCBbXSkgfD4gU2VxLnVuZm9sZCAoZnVuY3Rpb24KICAgIHwgW3x8XSwgXyAtPiBOb25lCiAgICB8IHdvcmRzLCBmaXJzdCAtPgogICAgICAgIG1hdGNoIEFycmF5LnRyeUZpbmRJbmRleCAoKD0pIGxjbVdvcmQpIHdvcmRzIHdpdGgKICAgICAgICB8IFNvbWUgaSAtPiBTb21lKGZpcnN0IEAgTGlzdC5vZkFycmF5IHdvcmRzLlsuLiBpXSwgKHdvcmRzLltpICsgMSAuLl0sIFtsY21Xb3JkXSkpCiAgICAgICAgfCBOb25lIC0+IFNvbWUoZmlyc3QgQCBMaXN0Lm9mQXJyYXkgd29yZHMsIChbfHxdLCBbbGNtV29yZF0pKSkKICAgIHw+IFNlcS50b0FycmF5CiAgICB8PiBBcnJheS5tYXBpICgoKikgbGNtTnVtYmVyID4+IGdldFBhcnRpYWxSZXN1bHQpCiAgICB8PiBmdW5jdGlvbgogICAgfCB4cyB3aGVuIEFycmF5LmZvcmFsbCBPcHRpb24uaXNTb21lIHhzIC0+IFNvbWUoeHMuWzBdLlZhbHVlLlsxXSwgeHMuW3hzLkxlbmd0aCAtIDFdLlZhbHVlLlswXSkKICAgIHwgXyAtPiBOb25lCgpbCiAgICBbImZpenoiXQogICAgWyJidXp6Il0KICAgIFsiZml6emJ1enoiXQogICAgWyJmaXp6IjsgImJ1enoiXQogICAgWyJidXp6IjsgImZpenoiXQogICAgWyJmaXp6IjsgImJ1enoiOyAiZml6eiJdCiAgICBbImZpenoiOyAiZml6eiJdCiAgICBbImZpenoiOyAiZml6eiI7ICJidXp6Il0KICAgIFsiYnV6eiI7ICJidXp6Il0KICAgIFsiZml6eiI7ICJmaXp6YnV6eiJdCiAgICBbImZpenpidXp6IjsgImZpenoiXQogICAgWyJmaXp6IjsgImZpenpidXp6IjsgImZpenoiXQogICAgWyJmaXp6YnV6eiI7ICJmaXp6IjsgImJ1enoiOyAiZml6eiI7ICJmaXp6IjsgImJ1enoiOyAiZml6eiI7ICJmaXp6YnV6eiJdCiAgICBbImZpenoiOyAiZml6emJ1enoiOyAiZml6eiI7ICJidXp6IjsgImZpenoiOyAiZml6eiI7ICJidXp6IjsgImZpenoiOyAiZml6emJ1enoiOyAiZml6eiI7ICJidXp6IjsgImZpenoiOyAiZml6eiI7ICJidXp6IjsgImZpenoiOyAiZml6emJ1enoiOyAiZml6eiI7ICJidXp6Il0KICAgIFsiIl0KICAgIFsiYWJjIl0KXSB8PiBMaXN0Lm1hcCBpbnZlcnNlRml6ekJ1enogfD4gTGlzdC56aXAgWwogICAgU29tZSgzLCAzKQogICAgU29tZSg1LCA1KQogICAgU29tZSgxNSwgMTUpCiAgICBTb21lKDksIDEwKQogICAgU29tZSg1LCA2KQogICAgU29tZSgzLCA2KQogICAgU29tZSg2LCA5KQogICAgU29tZSg2LCAxMCkKICAgIE5vbmUKICAgIFNvbWUoMTIsIDE1KQogICAgU29tZSgxNSwgMTgpCiAgICBTb21lKDEyLCAxOCkKICAgIFNvbWUoMTUsIDMwKQogICAgU29tZSgxMiwgNTApCiAgICBOb25lCiAgICBOb25lCl0gfD4gTGlzdC50cnlGaW5kICgoPHx8KSAoPD4pKQp8PiBwcmludGZuICIlQSI=