#lang racket
(require (only-in srfi/1 alist-cons alist-delete unfold))
(provide compress)
(define (createValue2Code data)
;; 各要素の発生回数を数える
(define (countDatum) ;; ローカル関数の場合、スコープが createValue2Code の data を捕捉する
(map (match-lambda
((cons n c) `(,c ,n)))
(sort
(foldl (lambda (elt lst)
(cond ((assv elt lst)
=> (lambda (x)
(alist-cons elt (add1 (cdr x))
(alist-delete elt lst))))
(else (alist-cons elt 1 lst))))
'() (string->list data)) #:key cdr <)))
;; ハフマン木を生成
(define (generateHuffman lst)
(foldl (lambda (elt lst)
(if (null? lst)
elt
`(,(+ (car elt) (car lst)) (,elt ,lst))))
'() lst))
;; 符号情報を作成
(define (codeInfo n)
(apply + (map (lambda (x)
(expt 2 x)) (range 1 (add1 n)))))
(define value2code
(case-lambda
((tree acc)
(match-let ((`(,x ,xs) tree))
(if (char? xs)
(foldl (lambda (y x)
(match-let ((`(,parent (,left '())) y))
`(,parent (,left ,x))))
`(,(add1 (codeInfo (sub1 x))) ,xs) acc)
(match-let ((`((,m ,left) (,n ,right)) xs))
(let ((k (codeInfo x)))
(value2code `(,(add1 x) ,right)
(cons `(,x ((,k ,left) '())) acc)))))))
((tree)
(value2code tree '()))))
(define dfs
(case-lambda
((tree acc)
(match-let ((`(,a (,b ,bs)) tree))
(match-let ((`(,x ,xs) bs))
(if (char? xs)
(let ((lst (reverse `(,bs ,b ,@acc))))
(values (map (match-lambda (`(,x ,xs) (cons xs x))) lst)
(map (match-lambda (`(,x ,xs) (cons x xs))) lst)))
(dfs bs (cons b acc))))))
((tree)
(dfs tree '()))))
(dfs (value2code (cons 0 (cdr (generateHuffman (countDatum)))))))
(define (compress data)
(let-values (((compress-table uncompress-table) (createValue2Code data)))
(values (map (lambda (key)
(cdr (assv key compress-table))) (string->list data))
uncompress-table)))
I2xhbmcgcmFja2V0CgoocmVxdWlyZSAob25seS1pbiBzcmZpLzEgYWxpc3QtY29ucyBhbGlzdC1kZWxldGUgdW5mb2xkKSkKKHByb3ZpZGUgY29tcHJlc3MpCgooZGVmaW5lIChjcmVhdGVWYWx1ZTJDb2RlIGRhdGEpCiAgOzsg5ZCE6KaB57Sg44Gu55m655Sf5Zue5pWw44KS5pWw44GI44KLCiAgKGRlZmluZSAoY291bnREYXR1bSkgOzsg44Ot44O844Kr44Or6Zai5pWw44Gu5aC05ZCI44CB44K544Kz44O844OX44GMIGNyZWF0ZVZhbHVlMkNvZGUg44GuIGRhdGEg44KS5o2V5o2J44GZ44KLCiAgICAobWFwIChtYXRjaC1sYW1iZGEKICAgICAgICAgICAoKGNvbnMgbiBjKSBgKCxjICxuKSkpCiAgICAgICAgIChzb3J0CiAgICAgICAgICAoZm9sZGwgKGxhbWJkYSAoZWx0IGxzdCkKICAgICAgICAgICAgICAgICAgIChjb25kICgoYXNzdiBlbHQgbHN0KQogICAgICAgICAgICAgICAgICAgICAgICAgID0+IChsYW1iZGEgKHgpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoYWxpc3QtY29ucyBlbHQgKGFkZDEgKGNkciB4KSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChhbGlzdC1kZWxldGUgZWx0IGxzdCkpKSkKICAgICAgICAgICAgICAgICAgICAgICAgIChlbHNlIChhbGlzdC1jb25zIGVsdCAxIGxzdCkpKSkKICAgICAgICAgICAgICAgICAnKCkgKHN0cmluZy0+bGlzdCBkYXRhKSkgIzprZXkgY2RyIDwpKSkKICA7OyDjg4/jg5Xjg57jg7PmnKjjgpLnlJ/miJAKICAoZGVmaW5lIChnZW5lcmF0ZUh1ZmZtYW4gbHN0KQogICAgKGZvbGRsIChsYW1iZGEgKGVsdCBsc3QpCiAgICAgICAgICAgICAoaWYgKG51bGw/IGxzdCkKICAgICAgICAgICAgICAgICBlbHQKICAgICAgICAgICAgICAgICBgKCwoKyAoY2FyIGVsdCkgKGNhciBsc3QpKSAoLGVsdCAsbHN0KSkpKQogICAgICAgICAgICcoKSBsc3QpKQogIDs7IOespuWPt+aDheWgseOCkuS9nOaIkAogIChkZWZpbmUgKGNvZGVJbmZvIG4pCiAgKGFwcGx5ICsgKG1hcCAobGFtYmRhICh4KQogICAgICAgICAgICAgICAgICAoZXhwdCAyIHgpKSAocmFuZ2UgMSAoYWRkMSBuKSkpKSkKICAoZGVmaW5lIHZhbHVlMmNvZGUKICAgIChjYXNlLWxhbWJkYQogICAgICAoKHRyZWUgYWNjKQogICAgICAgKG1hdGNoLWxldCAoKGAoLHggLHhzKSB0cmVlKSkKICAgICAgICAgKGlmIChjaGFyPyB4cykKICAgICAgICAgICAgIChmb2xkbCAobGFtYmRhICh5IHgpCiAgICAgICAgICAgICAgICAgICAgICAobWF0Y2gtbGV0ICgoYCgscGFyZW50ICgsbGVmdCAnKCkpKSB5KSkKICAgICAgICAgICAgICAgICAgICAgICAgYCgscGFyZW50ICgsbGVmdCAseCkpKSkKICAgICAgICAgICAgICAgICAgICBgKCwoYWRkMSAoY29kZUluZm8gKHN1YjEgeCkpKSAseHMpIGFjYykKICAgICAgICAgICAgIChtYXRjaC1sZXQgKChgKCgsbSAsbGVmdCkgKCxuICxyaWdodCkpIHhzKSkKICAgICAgICAgICAgICAgKGxldCAoKGsgKGNvZGVJbmZvIHgpKSkKICAgICAgICAgICAgICAgICAodmFsdWUyY29kZSBgKCwoYWRkMSB4KSAscmlnaHQpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKGNvbnMgYCgseCAoKCxrICxsZWZ0KSAnKCkpKSBhY2MpKSkpKSkpCiAgICAgICgodHJlZSkKICAgICAgICh2YWx1ZTJjb2RlIHRyZWUgJygpKSkpKQogIChkZWZpbmUgZGZzCiAgICAoY2FzZS1sYW1iZGEKICAgICAgKCh0cmVlIGFjYykKICAgICAgIChtYXRjaC1sZXQgKChgKCxhICgsYiAsYnMpKSB0cmVlKSkKICAgICAgICAgKG1hdGNoLWxldCAoKGAoLHggLHhzKSBicykpCiAgICAgICAgICAgKGlmIChjaGFyPyB4cykKICAgICAgICAgICAgICAgKGxldCAoKGxzdCAocmV2ZXJzZSBgKCxicyAsYiAsQGFjYykpKSkKICAgICAgICAgICAgICAgICAodmFsdWVzIChtYXAgKG1hdGNoLWxhbWJkYSAoYCgseCAseHMpIChjb25zIHhzIHgpKSkgbHN0KQogICAgICAgICAgICAgICAgICAgICAgICAgKG1hcCAobWF0Y2gtbGFtYmRhIChgKCx4ICx4cykgKGNvbnMgeCB4cykpKSBsc3QpKSkKICAgICAgICAgICAgICAgKGRmcyBicyAoY29ucyBiIGFjYykpKSkpKQogICAgICAoKHRyZWUpCiAgICAgICAoZGZzIHRyZWUgJygpKSkpKSAgCiAgKGRmcyAodmFsdWUyY29kZSAoY29ucyAwIChjZHIgKGdlbmVyYXRlSHVmZm1hbiAoY291bnREYXR1bSkpKSkpKSkKCihkZWZpbmUgKGNvbXByZXNzIGRhdGEpCiAgKGxldC12YWx1ZXMgKCgoY29tcHJlc3MtdGFibGUgdW5jb21wcmVzcy10YWJsZSkgKGNyZWF0ZVZhbHVlMkNvZGUgZGF0YSkpKQogICAgKHZhbHVlcyAobWFwIChsYW1iZGEgKGtleSkKICAgICAgICAgICAgICAgICAgIChjZHIgKGFzc3Yga2V5IGNvbXByZXNzLXRhYmxlKSkpIChzdHJpbmctPmxpc3QgZGF0YSkpCiAgICAgICAgICAgIHVuY29tcHJlc3MtdGFibGUpKSk=