(.using [library [lux "*" [abstract [monad {"+" do}] [codec {"+" Codec}] [equivalence {"+" Equivalence}] [order {"+" Order}]] [control ["[0]" try {"+" Try}] ["[0]" exception {"+" exception:}] ["<>" parser ["<[0]>" text {"+" Parser}]]] [data ["[0]" text ("[1]#[0]" monoid)]] [math [number ["n" nat ("[1]#[0]" decimal)] ["i" int ("[1]#[0]" decimal)]]] [type abstract]]]) (def: (internal year) (-> Int Int) (if (i.< +0 year) (++ year) year)) (def: (external year) (-> Int Int) (if (i.> +0 year) year (-- year))) (exception: .public there_is_no_year_0) ... https://en.wikipedia.org/wiki/Gregorian_calendar (abstract: .public Year Int (def: .public (year value) (-> Int (Try Year)) (case value +0 (exception.except ..there_is_no_year_0 []) _ {try.#Success (:abstraction (..internal value))})) (def: .public value (-> Year Int) (|>> :representation ..external)) (def: .public epoch Year (:abstraction +1970)) ) (def: .public days Nat 365) (type: .public Period Nat) (template [ ] [(def: .public Period )] [004 leap] [100 century] [400 era] ) (def: (divisible? factor input) (-> Int Int Bit) (|> input (i.% factor) (i.= +0))) ... https://en.wikipedia.org/wiki/Leap_year#Algorithm (def: .public (leap? year) (-> Year Bit) (let [year (|> year ..value ..internal)] (and (..divisible? (.int ..leap) year) (or (not (..divisible? (.int ..century) year)) (..divisible? (.int ..era) year))))) (def: (with_year_0_leap year days) (let [after_year_0? (i.> +0 year)] (if after_year_0? (i.+ +1 days) days))) (def: .public (leaps year) (-> Year Int) (let [year (|> year ..value ..internal) limit (if (i.> +0 year) (-- year) (++ year))] (`` (|> +0 (~~ (template [ ] [( (i./ (.int ) limit))] [i.+ ..leap] [i.- ..century] [i.+ ..era] )) (..with_year_0_leap year))))) (def: (encoded year) (-> Year Text) (let [year (..value year)] (if (i.< +0 year) (i#encoded year) (n#encoded (.nat year))))) (def: .public parser (Parser Year) (do [! <>.monad] [sign (<>.or (.this "-") (in [])) digits (.many .decimal) raw_year (<>.codec i.decimal (in (text#composite "+" digits)))] (<>.lifted (..year (case sign {.#Left _} (i.* -1 raw_year) {.#Right _} raw_year))))) (implementation: .public codec (Codec Text Year) (def: encoded ..encoded) (def: decoded (.result ..parser))) (implementation: .public equivalence (Equivalence Year) (def: (= reference subject) (i.= (..value reference) (..value subject)))) (implementation: .public order (Order Year) (def: &equivalence ..equivalence) (def: (< reference subject) (i.< (..value reference) (..value subject))))