import Control
.Monad.Trans
.State
.Lazy
import Control
.Monad (foldM
)
data Input = Plus | Minus | ToggleNegate | ToggleEnabled
type Accum = [Emission]
type Output = [Emission]
toInput '+' = Plus
toInput '-' = Minus
toInput 'n' = ToggleNegate
toInput 'e' = ToggleEnabled
toInput
_ = error "Invalid input representation"
-- Determine new state of state machine along with transition emission
step :: (Negated, Enabled, Input) -> (Negated, Enabled, Emission)
step
(n
, e
, ToggleNegate
) = (not n
, e
, 0)step
(n
, e
, ToggleEnabled
) = (n
, not e
, 0)step (n, False, i) = (n, False, 0)
step (n, e, Plus) = (n, e, if n then -1 else 1)
step (n, e, Minus) = (n, e, if n then 1 else -1)
-- Helper function for "evaluate"'s foldM
mapEmissions :: Accum -> Input -> State (Negated, Enabled) Output
mapEmissions accum input = do
(negated, enabled) <- get
let (negated', enabled', emission) = step (negated, enabled, input)
put (negated', enabled')
-- Process an input string and return the result
-- (False, True) is the start state: (not negated, enabled)
evaluate :: [Input] -> Output
evaluate inputs = evalState (foldM mapEmissions [] inputs) (False, True)
-- Convenience function for output formatting
shouldEqual input expected = do
let actual
= (sum . evaluate
. map toInput
) input
putStrLn $ "Expected " ++ show expected
++ ", got " ++ show actual
++ ": " ++ input
main = do
"+-n--n" `shouldEqual` 2
"+e----e++" `shouldEqual` 3
"-n++e++e--+-n++" `shouldEqual` 1
aW1wb3J0IENvbnRyb2wuTW9uYWQuVHJhbnMuU3RhdGUuTGF6eQppbXBvcnQgQ29udHJvbC5Nb25hZCAoZm9sZE0pCgpkYXRhIElucHV0ID0gUGx1cyB8IE1pbnVzIHwgVG9nZ2xlTmVnYXRlIHwgVG9nZ2xlRW5hYmxlZAp0eXBlIEVtaXNzaW9uID0gSW50ZWdlcgp0eXBlIEFjY3VtID0gW0VtaXNzaW9uXQp0eXBlIE91dHB1dCA9IFtFbWlzc2lvbl0KdHlwZSBOZWdhdGVkID0gQm9vbAp0eXBlIEVuYWJsZWQgPSBCb29sCgp0b0lucHV0IDo6IENoYXIgLT4gSW5wdXQKdG9JbnB1dCAnKycgPSBQbHVzCnRvSW5wdXQgJy0nID0gTWludXMKdG9JbnB1dCAnbicgPSBUb2dnbGVOZWdhdGUKdG9JbnB1dCAnZScgPSBUb2dnbGVFbmFibGVkCnRvSW5wdXQgIF8gID0gZXJyb3IgIkludmFsaWQgaW5wdXQgcmVwcmVzZW50YXRpb24iCgotLSBEZXRlcm1pbmUgbmV3IHN0YXRlIG9mIHN0YXRlIG1hY2hpbmUgYWxvbmcgd2l0aCB0cmFuc2l0aW9uIGVtaXNzaW9uCnN0ZXAgOjogKE5lZ2F0ZWQsIEVuYWJsZWQsIElucHV0KSAtPiAoTmVnYXRlZCwgRW5hYmxlZCwgRW1pc3Npb24pCnN0ZXAgKG4sIGUsIFRvZ2dsZU5lZ2F0ZSkgID0gKG5vdCBuLCBlLCAwKQpzdGVwIChuLCBlLCBUb2dnbGVFbmFibGVkKSA9IChuLCBub3QgZSwgMCkKc3RlcCAobiwgRmFsc2UsIGkpICAgICAgICAgPSAobiwgRmFsc2UsIDApCnN0ZXAgKG4sIGUsIFBsdXMpICAgICAgICAgID0gKG4sIGUsIGlmIG4gdGhlbiAtMSBlbHNlICAxKQpzdGVwIChuLCBlLCBNaW51cykgICAgICAgICA9IChuLCBlLCBpZiBuIHRoZW4gIDEgZWxzZSAtMSkKCi0tIEhlbHBlciBmdW5jdGlvbiBmb3IgImV2YWx1YXRlIidzIGZvbGRNCm1hcEVtaXNzaW9ucyA6OiBBY2N1bSAtPiBJbnB1dCAtPiBTdGF0ZSAoTmVnYXRlZCwgRW5hYmxlZCkgT3V0cHV0Cm1hcEVtaXNzaW9ucyBhY2N1bSBpbnB1dCA9IGRvCiAgICAobmVnYXRlZCwgZW5hYmxlZCkgPC0gZ2V0CiAgICBsZXQgKG5lZ2F0ZWQnLCBlbmFibGVkJywgZW1pc3Npb24pID0gc3RlcCAobmVnYXRlZCwgZW5hYmxlZCwgaW5wdXQpCiAgICBwdXQgKG5lZ2F0ZWQnLCBlbmFibGVkJykKICAgIHJldHVybiAoYWNjdW0gKysgW2VtaXNzaW9uXSkKCi0tIFByb2Nlc3MgYW4gaW5wdXQgc3RyaW5nIGFuZCByZXR1cm4gdGhlIHJlc3VsdAotLSAoRmFsc2UsIFRydWUpIGlzIHRoZSBzdGFydCBzdGF0ZTogKG5vdCBuZWdhdGVkLCBlbmFibGVkKQpldmFsdWF0ZSA6OiBbSW5wdXRdIC0+IE91dHB1dApldmFsdWF0ZSBpbnB1dHMgPSBldmFsU3RhdGUgKGZvbGRNIG1hcEVtaXNzaW9ucyBbXSBpbnB1dHMpIChGYWxzZSwgVHJ1ZSkKCgotLSBDb252ZW5pZW5jZSBmdW5jdGlvbiBmb3Igb3V0cHV0IGZvcm1hdHRpbmcKc2hvdWxkRXF1YWwgOjogU3RyaW5nIC0+IEludGVnZXIgLT4gSU8gKCkKc2hvdWxkRXF1YWwgaW5wdXQgZXhwZWN0ZWQgPSBkbwogICAgbGV0IGFjdHVhbCA9IChzdW0gLiBldmFsdWF0ZSAuIG1hcCB0b0lucHV0KSBpbnB1dAogICAgcHV0U3RyTG4gJCAiRXhwZWN0ZWQgIiArKyBzaG93IGV4cGVjdGVkICsrICIsIGdvdCAiICsrIHNob3cgYWN0dWFsICsrICI6ICIgKysgaW5wdXQKCm1haW4gOjogSU8gKCkKbWFpbiA9IGRvCiAgICAiKy1uLS1uIiBgc2hvdWxkRXF1YWxgIDIKICAgICIrZS0tLS1lKysiIGBzaG91bGRFcXVhbGAgMwogICAgIi1uKytlKytlLS0rLW4rKyIgYHNob3VsZEVxdWFsYCAx