--
-- Problem: Find the 51000000000-th character of the string (wordNumber Infinity)
-- where a wordNumber is defined as
--
-- wordNumber 1 = "one"
-- wordNumber 2 = "onetwo"
-- wordNumber 3 = "onetwothree"
-- wordNumber 15 = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourteenfifteen"
-- ...
--
-- The answer should be presented as ( sum of all numbers up to that point
-- , the 51000000000-th character
-- )
{-# LANGUAGE BangPatterns #-}
import Debug.Trace
import Data.Array.Unboxed
ones = ["", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
tens = ["", "ten", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]
teens = ["ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"]
lenOnes, lenTens, lenTeens :: UArray Int64 Int64
lenOnes = listArray (0,9) $ [0,3,3,5,4,5,3,5,5,4] -- "", "one","two", ...
lenTens = listArray (0,9) $ [0,3,6,6,5,5,5,7,6,6]
lenTeens = listArray (0,9) $ [3,6,6,8,8,7,7,9,8,8] -- first element is "ten" 3
-- potentially cleaner version
-- but I feared Haskell might do surprising things behind my
-- back so I stuck with the above
-- lenBelowHundred = listArray (0,99) $ map (fromIntegral . length . wordify) [1..99]
-- wordify 123 = "onehundredtwentythree"
-- This is only used once in presenting the final result character
wordify n
| n < 100 = splitterTen
| n < 1000 = splitter 100 "hundred"
| n < 1000000 = splitter 1000 "thousand"
| n < 1000000000 = splitter 1000000 "million"
where
splitterTen
= let (t
, x
) = n `
divMod`
10 in (wordify t) ++ suffix ++ wordify x
-- Optimized version of length (wordify n)
-- Used in number crunching
wordLength n = wordLength' 0 n
-- Tail recursive version
wordLength' :: Int64 -> Int64 -> Int64
wordLength' !pad !n
| n < 10 = lenOnes ! n + pad
| n < 20 = lenTeens ! (n-10) + pad
| n < 100 = splitterTen
| n < 1000 = splitter 100 7
| n < 1000000 = splitter 1000 8
| otherwise = splitter 1000000 7
where
splitterTen = let !(!t, !x) = n `divMod` 10
in wordLength' (lenTens ! t + pad) x
splitter
!d
!suffix
= let !(!t
, !x
) = n `
divMod` d
in wordLength' (wordLength' (suffix+pad) t) x
-- Tail recursive
solve :: Int64 -> (Int64, Int64, Int64) -> [Int64] -> (Int64, Int64, Int64)
solve !n !acc@(!sumNum, !sumLen, !curr) (!num:nums)
| sumLen' >= n = (sumNum', sumLen, num)
| otherwise = solve n
(sumNum
', sumLen', num
) nums
where
sumNum' = sumNum + num
sumLen' = sumLen + wordLength num
solution
:: Int64
-> (Int64
, Char)solution !x =
let (sumNum, sumLen, n) = solve x (0,0,1) [1..]
main = do
print $ solution
1234 -- Make sure we are sane print $ solution
51000000000
LS0KLS0gUHJvYmxlbTogRmluZCB0aGUgNTEwMDAwMDAwMDAtdGggY2hhcmFjdGVyIG9mIHRoZSBzdHJpbmcgKHdvcmROdW1iZXIgSW5maW5pdHkpCi0tIHdoZXJlIGEgd29yZE51bWJlciBpcyBkZWZpbmVkIGFzCi0tCi0tIHdvcmROdW1iZXIgMSA9ICJvbmUiCi0tIHdvcmROdW1iZXIgMiA9ICJvbmV0d28iCi0tIHdvcmROdW1iZXIgMyA9ICJvbmV0d290aHJlZSIKLS0gd29yZE51bWJlciAxNSA9ICJvbmV0d290aHJlZWZvdXJmaXZlc2l4c2V2ZW5laWdodG5pbmV0ZW5lbGV2ZW50d2VsdmV0aGlydGVlbmZvdXJ0ZWVuZmlmdGVlbiIKLS0gLi4uCi0tCi0tIFRoZSBhbnN3ZXIgc2hvdWxkIGJlIHByZXNlbnRlZCBhcyAoIHN1bSBvZiBhbGwgbnVtYmVycyB1cCB0byB0aGF0IHBvaW50Ci0tICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsIHRoZSA1MTAwMDAwMDAwMC10aCBjaGFyYWN0ZXIKLS0gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnstIyBMQU5HVUFHRSBCYW5nUGF0dGVybnMgIy19CgppbXBvcnQgRGVidWcuVHJhY2UKaW1wb3J0IERhdGEuSW50CmltcG9ydCBDb250cm9sLk1vbmFkCmltcG9ydCBEYXRhLkFycmF5LlVuYm94ZWQKCm9uZXMgPSBbIiIsICJvbmUiLCAidHdvIiwgInRocmVlIiwgImZvdXIiLCAiZml2ZSIsICJzaXgiLCAic2V2ZW4iLCAiZWlnaHQiLCAibmluZSJdCnRlbnMgPSBbIiIsICJ0ZW4iLCAidHdlbnR5IiwgInRoaXJ0eSIsICJmb3J0eSIsICJmaWZ0eSIsICJzaXh0eSIsICJzZXZlbnR5IiwgImVpZ2h0eSIsICJuaW5ldHkiXQp0ZWVucyA9IFsidGVuIiwgImVsZXZlbiIsICJ0d2VsdmUiLCAidGhpcnRlZW4iLCAiZm91cnRlZW4iLCAiZmlmdGVlbiIsICJzaXh0ZWVuIiwgInNldmVudGVlbiIsICJlaWdodGVlbiIsICJuaW5ldGVlbiJdCgpsZW5PbmVzLCBsZW5UZW5zLCBsZW5UZWVucyA6OiBVQXJyYXkgSW50NjQgSW50NjQKbGVuT25lcyA9IGxpc3RBcnJheSAoMCw5KSAkIFswLDMsMyw1LDQsNSwzLDUsNSw0XSAtLSAiIiwgIm9uZSIsInR3byIsIC4uLgpsZW5UZW5zID0gbGlzdEFycmF5ICgwLDkpICQgWzAsMyw2LDYsNSw1LDUsNyw2LDZdCmxlblRlZW5zID0gbGlzdEFycmF5ICgwLDkpICQgWzMsNiw2LDgsOCw3LDcsOSw4LDhdIC0tIGZpcnN0IGVsZW1lbnQgaXMgInRlbiIgMwoKLS0gcG90ZW50aWFsbHkgY2xlYW5lciB2ZXJzaW9uCi0tIGJ1dCBJIGZlYXJlZCBIYXNrZWxsIG1pZ2h0IGRvIHN1cnByaXNpbmcgdGhpbmdzIGJlaGluZCBteSAKLS0gYmFjayBzbyBJIHN0dWNrIHdpdGggdGhlIGFib3ZlCi0tIGxlbkJlbG93SHVuZHJlZCA9IGxpc3RBcnJheSAoMCw5OSkgJCBtYXAgKGZyb21JbnRlZ3JhbCAuIGxlbmd0aCAuIHdvcmRpZnkpIFsxLi45OV0KCi0tIHdvcmRpZnkgMTIzID0gIm9uZWh1bmRyZWR0d2VudHl0aHJlZSIKLS0gVGhpcyBpcyBvbmx5IHVzZWQgb25jZSBpbiBwcmVzZW50aW5nIHRoZSBmaW5hbCByZXN1bHQgY2hhcmFjdGVyCndvcmRpZnkgOjogSW50NjQgLT4gU3RyaW5nCndvcmRpZnkgbgogICAgfCBuIDwgMTAgICAgICAgICA9IG9uZXMgISEgZnJvbUludGVncmFsIG4KICAgIHwgbiA8IDIwICAgICAgICAgPSB0ZWVucyAhISAoZnJvbUludGVncmFsIG4tMTApCiAgICB8IG4gPCAxMDAgICAgICAgID0gc3BsaXR0ZXJUZW4KICAgIHwgbiA8IDEwMDAgICAgICAgPSBzcGxpdHRlciAxMDAgImh1bmRyZWQiCiAgICB8IG4gPCAxMDAwMDAwICAgID0gc3BsaXR0ZXIgMTAwMCAidGhvdXNhbmQiCiAgICB8IG4gPCAxMDAwMDAwMDAwID0gc3BsaXR0ZXIgMTAwMDAwMCAibWlsbGlvbiIKICAgIHdoZXJlCiAgICAgICAgc3BsaXR0ZXJUZW4gPSBsZXQgKHQsIHgpID0gbiBgZGl2TW9kYCAxMAogICAgICAgICAgICAgICAgICAgICAgaW4gKHRlbnMgISEgZnJvbUludGVncmFsIHQpICsrIHdvcmRpZnkgeAogICAgICAgIHNwbGl0dGVyIGRpdiBzdWZmaXggPSBsZXQgKHQsIHgpID0gbiBgZGl2TW9kYCBkaXYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW4gKHdvcmRpZnkgdCkgKysgc3VmZml4ICsrIHdvcmRpZnkgeAoKLS0gT3B0aW1pemVkIHZlcnNpb24gb2YgbGVuZ3RoICh3b3JkaWZ5IG4pCi0tIFVzZWQgaW4gbnVtYmVyIGNydW5jaGluZwp3b3JkTGVuZ3RoIG4gPSB3b3JkTGVuZ3RoJyAwIG4KCi0tIFRhaWwgcmVjdXJzaXZlIHZlcnNpb24Kd29yZExlbmd0aCcgOjogSW50NjQgLT4gSW50NjQgLT4gSW50NjQKd29yZExlbmd0aCcgIXBhZCAhbgogICAgfCBuIDwgMTAgICAgICAgICA9IGxlbk9uZXMgISBuICsgcGFkCiAgICB8IG4gPCAyMCAgICAgICAgID0gbGVuVGVlbnMgISAobi0xMCkgKyBwYWQKICAgIHwgbiA8IDEwMCAgICAgICAgPSBzcGxpdHRlclRlbgogICAgfCBuIDwgMTAwMCAgICAgICA9IHNwbGl0dGVyIDEwMCA3CiAgICB8IG4gPCAxMDAwMDAwICAgID0gc3BsaXR0ZXIgMTAwMCA4CiAgICB8IG90aGVyd2lzZSAgICAgID0gc3BsaXR0ZXIgMTAwMDAwMCA3CiAgICB3aGVyZQogICAgICAgIHNwbGl0dGVyVGVuID0gbGV0ICEoIXQsICF4KSA9ICBuIGBkaXZNb2RgIDEwCiAgICAgICAgICAgICAgICAgICAgICBpbiB3b3JkTGVuZ3RoJyAobGVuVGVucyAhIHQgKyBwYWQpIHgKICAgICAgICBzcGxpdHRlciAhZCAhc3VmZml4ID0gbGV0ICEoIXQsICF4KSA9IG4gYGRpdk1vZGAgZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbiB3b3JkTGVuZ3RoJyAod29yZExlbmd0aCcgKHN1ZmZpeCtwYWQpIHQpIHgKCi0tIFRhaWwgcmVjdXJzaXZlCnNvbHZlIDo6IEludDY0IC0+IChJbnQ2NCwgSW50NjQsIEludDY0KSAtPiBbSW50NjRdIC0+IChJbnQ2NCwgSW50NjQsIEludDY0KQpzb2x2ZSAhbiAhYWNjQCghc3VtTnVtLCAhc3VtTGVuLCAhY3VycikgKCFudW06bnVtcykKICAgIHwgc3VtTGVuJyA+PSBuID0gKHN1bU51bScsIHN1bUxlbiwgbnVtKQogICAgfCBvdGhlcndpc2UgPSBzb2x2ZSBuIChzdW1OdW0nLCBzdW1MZW4nLCBudW0pIG51bXMKICAgIHdoZXJlCiAgICAgICAgc3VtTnVtJyA9IHN1bU51bSArIG51bQogICAgICAgIHN1bUxlbicgPSBzdW1MZW4gKyB3b3JkTGVuZ3RoIG51bQoKc29sdXRpb24gOjogSW50NjQgLT4gKEludDY0LCBDaGFyKQpzb2x1dGlvbiAheCA9CiAgICBsZXQgKHN1bU51bSwgc3VtTGVuLCBuKSA9IHNvbHZlIHggKDAsMCwxKSBbMS4uXQogICAgaW4gKHN1bU51bSwgKHdvcmRpZnkgbikgISEgKGZyb21JbnRlZ3JhbCAkIHggLSBzdW1MZW4gLSAxKSkKCm1haW4gPSBkbwogICAgcHJpbnQgJCBzb2x1dGlvbiAxMjM0IC0tIE1ha2Ugc3VyZSB3ZSBhcmUgc2FuZQogICAgcHJpbnQgJCBzb2x1dGlvbiA1MTAwMDAwMDAwMAoK