Traditionally, the Chinese have counted years using two simultaneous cycles, one of length 10 (the "celestial stems") and one of length 12 (the "terrestrial branches"); the combination results in a repeating 60-year pattern. Mapping the branches to twelve traditional animal deities results in the well-known "Chinese zodiac", assigning each year to a given animal. For example, Tuesday, February 1, 2022 CE (in the common Gregorian calendar) will begin the lunisolar Year of the Tiger.

The celestial stems have no one-to-one mapping like that of the branches to animals; however, the five pairs of consecutive stems each belong to one of the five traditional Chinese elements (Wood, Fire, Earth, Metal, and Water). Further, one of the two years within each element's governance is associated with yin, the other with yang.

Thus, 2022 is also the yang year of Water. Note that since 12 is an even number, the association between animals and yin/yang doesn't change. Consecutive Years of the Rooster will cycle through the five elements, but will always be yin, despite the apparent conceptual mismatch between the specifically-male English animal name and the female aspect denoted by yin.

Task
 Create a subroutine or program that will return or output the animal, yin/yang association, and element for the lunisolar year that begins in a given CE year.
You may optionally provide more information in the form of the year's numerical position within the 60-year cycle and/or its actual Chinese stem-branch name (in Han characters or Pinyin transliteration).

Requisite information

 The animal cycle runs in this order: Rat, Ox, Tiger, Rabbit, Dragon, Snake, Horse, Goat, Monkey, Rooster, Dog, Pig.
 The element cycle runs in this order: Wood, Fire, Earth, Metal, Water.
 The yang year precedes the yin year within each element.
 The current 60-year cycle began in 1984 CE; the first cycle of the Common Era began in 4 CE.
Thus, 1984 was the year of the Wood Rat (yang), 1985 was the year of the Wood Ox (yin), and 1986 the year of the Fire Tiger (yang); 2022 - which, as already noted, is the year of the Water Tiger (yang) - is the 39th year of the current cycle.

Information for optional task

 The ten celestial stems are 甲 jiă, 乙 yĭ, 丙 bĭng, 丁 dīng, 戊 wù, 己 jĭ, 庚 gēng, 辛 xīn, 壬 rén, and 癸 gŭi. With the ASCII version of Pinyin tones, the names are written "jia3", "yi3", "bing3", "ding1", "wu4", "ji3", "geng1", "xin1", "ren2", and "gui3".
 The twelve terrestrial branches are 子 zĭ, 丑 chŏu, 寅 yín, 卯 măo, 辰 chén, 巳 sì, 午 wŭ, 未 wèi, 申 shēn, 酉 yŏu, 戌 xū, 亥 hài. In ASCII Pinyin, those are "zi3", "chou3", "yin2", "mao3", "chen2", "si4", "wu3", "wei4", "shen1", "you3", "xu1", and "hai4".
Therefore 1984 was 甲子 (jiă-zĭ, or jia3-zi3). 2022 is 壬寅 (rén-yín or ren2-yin2).

on run
 
    -- TRADITIONAL STRINGS ---------------------------------------------------
 
    -- ts :: Array Int (String, String)            -- 天干 tiangan – 10 heavenly stems
    set ts to zip(chars("甲乙丙丁戊己庚辛壬癸"), ¬
        |words|("jiă yĭ bĭng dīng wù jĭ gēng xīn rén gŭi"))
 
    -- ds :: Array Int (String, String)            -- 地支 dizhi – 12 terrestrial branches
    set ds to zip(chars("子丑寅卯辰巳午未申酉戌亥"), ¬
        |words|("zĭ chŏu yín măo chén sì wŭ wèi shēn yŏu xū hài"))
 
    -- ws :: Array Int (String, String, String)    -- 五行 wuxing – 5 elements
    set ws to zip3(chars("木火土金水"), ¬
        |words|("mù huǒ tǔ jīn shuǐ"), ¬
        |words|("wood fire earth metal water"))
 
    -- xs :: Array Int (String, String, String)    -- 十二生肖 shengxiao – 12 symbolic animals
    set xs to zip3(chars("鼠牛虎兔龍蛇馬羊猴鸡狗豬"), ¬
        |words|("shǔ niú hǔ tù lóng shé mǎ yáng hóu jī gǒu zhū"), ¬
        |words|("rat ox tiger rabbit dragon snake horse goat monkey rooster dog pig"))
 
    -- ys :: Array Int (String, String)            -- 阴阳 yinyang
    set ys to zip(chars("阳阴"), |words|("yáng yīn"))
 
 
    -- TRADITIONAL CYCLES ----------------------------------------------------
 
    script cycles
        on |λ|(y)
            set iYear to y - 4
            set iStem to iYear mod 10
            set iBranch to iYear mod 12
            set {hStem, pStem} to item (iStem + 1) of ts
            set {hBranch, pBranch} to item (iBranch + 1) of ds
            set {hElem, pElem, eElem} to item ((iStem div 2) + 1) of ws
            set {hAnimal, pAnimal, eAnimal} to item (iBranch + 1) of xs
            set {hYinyang, pYinyang} to item ((iYear mod 2) + 1) of ys
 
            {{show(y), hStem & hBranch, hElem, hAnimal, hYinyang}, ¬
                {"", pStem & pBranch, pElem, pAnimal, pYinyang}, ¬
                {"", show((iYear mod 60) + 1) & "/60", eElem, eAnimal, ""}}
        end |λ|
    end script
 
    -- FORMATTING ------------------------------------------------------------
 
    -- fieldWidths :: [[Int]]
    set fieldWidths to {{6, 10, 7, 8, 3}, {6, 11, 8, 8, 4}, {6, 11, 8, 8, 4}}
 
    script showYear
        script widthStringPairs
            on |λ|(nscs)
                set {ns, cs} to nscs
                zip(ns, cs)
            end |λ|
        end script
 
        script justifiedRow
            on |λ|(row)
                script
                    on |λ|(ns)
                        set {n, s} to ns
                        justifyLeft(n, space, s)
                    end |λ|
                end script
 
                concat(map(result, row))
            end |λ|
        end script
 
        on |λ|(y)
            unlines(map(justifiedRow, ¬
                map(widthStringPairs, ¬
                    zip(fieldWidths, |λ|(y) of cycles))))
        end |λ|
    end script
 
    -- TEST OUTPUT -----------------------------------------------------------
    intercalate("\n\n", map(showYear, {1935, 1938, 1968, 1972, 1976, 1984, 2017}))
end run
 
 
-- GENERIC FUNCTIONS ---------------------------------------------------------
 
-- chars :: String -> [String]
on chars(s)
    characters of s
end chars
 
-- concat :: [[a]] -> [a] | [String] -> String
on concat(xs)
    if length of xs > 0 and class of (item 1 of xs) is string then
        set acc to ""
    else
        set acc to {}
    end if
    repeat with i from 1 to length of xs
        set acc to acc & item i of xs
    end repeat
    acc
end concat
 
-- foldl :: (a -> b -> a) -> a -> [b] -> a
on foldl(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with i from 1 to lng
            set v to |λ|(v, item i of xs, i, xs)
        end repeat
        return v
    end tell
end foldl
 
-- intercalate :: Text -> [Text] -> Text
on intercalate(strText, lstText)
    set {dlm, my text item delimiters} to {my text item delimiters, strText}
    set strJoined to lstText as text
    set my text item delimiters to dlm
    return strJoined
end intercalate
 
-- justifyLeft :: Int -> Char -> Text -> Text
on justifyLeft(n, cFiller, strText)
    if n > length of strText then
        text 1 thru n of (strText & replicate(n, cFiller))
    else
        strText
    end if
end justifyLeft
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map
 
-- min :: Ord a => a -> a -> a
on min(x, y)
    if y < x then
        y
    else
        x
    end if
end min
 
-- minimum :: [a] -> a 
on minimum(xs)
    script min
        on |λ|(a, x)
            if x < a or a is missing value then
                x
            else
                a
            end if
        end |λ|
    end script
 
    foldl(min, missing value, xs)
end minimum
 
-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn
 
-- Egyptian multiplication - progressively doubling a list, appending
-- stages of doubling to an accumulator where needed for binary 
-- assembly of a target length
 
-- replicate :: Int -> a -> [a]
on replicate(n, a)
    set out to {}
    if n < 1 then return out
    set dbl to {a}
 
    repeat while (n > 1)
        if (n mod 2) > 0 then set out to out & dbl
        set n to (n div 2)
        set dbl to (dbl & dbl)
    end repeat
    return out & dbl
end replicate
 
-- show :: a -> String
on show(e)
    set c to class of e
    if c = list then
        script serialized
            on |λ|(v)
                show(v)
            end |λ|
        end script
 
        "[" & intercalate(", ", map(serialized, e)) & "]"
    else if c = record then
        script showField
            on |λ|(kv)
                set {k, ev} to kv
                "\"" & k & "\":" & show(ev)
            end |λ|
        end script
 
        "{" & intercalate(", ", ¬
            map(showField, zip(allKeys(e), allValues(e)))) & "}"
    else if c = date then
        "\"" & iso8601Z(e) & "\""
    else if c = text then
        "\"" & e & "\""
    else if (c = integer or c = real) then
        e as text
    else if c = class then
        "null"
    else
        try
            e as text
        on error
            ("«" & c as text) & "»"
        end try
    end if
end show
 
-- unlines :: [String] -> String
on unlines(xs)
    intercalate(linefeed, xs)
end unlines
 
-- words :: String -> [String]
on |words|(s)
    words of s
end |words|
 
-- zip :: [a] -> [b] -> [(a, b)]
on zip(xs, ys)
    set lng to min(length of xs, length of ys)
    set lst to {}
    repeat with i from 1 to lng
        set end of lst to {item i of xs, item i of ys}
    end repeat
    return lst
end zip
 
-- zip3 :: [a] -> [b] -> [c] -> [(a, b, c)]
on zip3(xs, ys, zs)
    script
        on |λ|(x, i)
            [x, item i of ys, item i of zs]
        end |λ|
    end script
 
    map(result, items 1 thru ¬
        minimum({length of xs, length of ys, length of zs}) of xs)
end zip3