Last active
January 1, 2016 03:29
-
-
Save glts/8085844 to your computer and use it in GitHub Desktop.
Vim autoload library for base-k number systems without zero
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
" Tiny library for conversion of numbers of bijective base-k number systems | |
" (number systems without zero) to decimal integers and vice-versa. Examples: | |
" | |
" base 3: 1 2 3 11 12 13 21 22 23 31 32 33 111 112 113 ... | |
" base 5 (digits {a b c d e}): a b c d e aa ab ac ad ae ba ... | |
" | |
" The public interface is numerals#CreateConverter(digitstring) which creates | |
" a {converter} for the number system defined by the digitstring. | |
" | |
" {converter}.ValueOf(int) - converts an integer to a base-k number string | |
" {converter}.ToInteger(str) - converts a base-k number string to an integer | |
" {converter}.Inc(str) - increments a base-k number string by 1 | |
function! s:ValidNumberRegexp(digitstring) abort | |
let l:digitre = join(map(split(a:digitstring, '\zs'), 'escape(v:val,"\\")'), '\|') | |
return '\V\^\%(' . l:digitre . '\)\*\$' | |
endfunction | |
function! s:ValidDigitString(digitstring) abort | |
" Make sure all digits are unique | |
let l:seen = {} | |
for i in range(len(a:digitstring)) | |
if has_key(l:seen, a:digitstring[i]) | |
return 0 | |
endif | |
let l:seen[a:digitstring[i]] = 1 | |
endfor | |
return 1 | |
endfunction | |
function! s:ToInteger(str) dict abort | |
if a:str !~# self._validnrre | |
echohl ErrorMsg | |
echomsg "Invalid argument for this base" | |
echohl None | |
return -1 | |
endif | |
let l:result = 0 | |
let l:n = len(a:str) | |
for i in range(l:n) | |
let l:result += float2nr(pow(self._base, l:n-i-1)) * self._digitvals[a:str[i]] | |
endfor | |
return l:result | |
endfunction | |
function! s:ValueOf(int) dict abort | |
if a:int < 0 | |
echohl ErrorMsg | |
echomsg "Invalid argument for this base" | |
echohl None | |
return | |
endif | |
let l:basef = str2float(self._base . '.0') | |
let l:result = [] | |
let l:q = a:int | |
while l:q > 0 | |
let l:qnext = float2nr(ceil(l:q/l:basef)) - 1 | |
call insert(l:result, self._digits[l:q - l:qnext*self._base]) | |
let l:q = l:qnext | |
endwhile | |
return join(l:result, '') | |
endfunction | |
function! s:Inc(str) dict abort | |
if a:str !~# self._validnrre | |
echohl ErrorMsg | |
echomsg "Invalid argument for this base" | |
echohl None | |
return -1 | |
endif | |
let l:digits = split(a:str, '\zs') | |
let l:addone = 1 | |
let l:len = len(l:digits) | |
let i = 0 | |
while l:addone | |
let l:index = l:len - i - 1 | |
if l:index < 0 | |
" Add extra digit with value 1 in front | |
call insert(l:digits, self._digits[1]) | |
break | |
endif | |
let l:val = self._digitvals[l:digits[l:index]] | |
let l:addone = l:val is# self._base | |
let l:digits[l:index] = self._digits[l:val%self._base + 1] | |
let i += 1 | |
endwhile | |
return join(l:digits, '') | |
endfunction | |
" Takes a string with the digits of a bijective base-k number system, in order | |
" of their values 1 to k, and returns a new {converter} for that number | |
" system. The converter exposes the functions ValueOf() and ToInteger(), which | |
" convert from and to decimal, and the increment function Inc(). | |
" | |
" :let g:base3 = numerals#CreateConverter('123') | |
" :echomsg g:base3.ValueOf(42) | |
" ~ '1113' | |
" :echomsg g:base3.Inc('1113') | |
" ~ '1121' | |
" | |
" The function {converter}.ValueOf(int) takes an integer and returns the | |
" corresponding base-k representation as a string. | |
" Reports an error for negative integers and returns false (0). | |
" | |
" :let g:base26 = numerals#CreateConverter('abcdefghijklmnopqrstuvwxyz') | |
" :echomsg g:base26.ValueOf(27) | |
" ~ 'aa' | |
" | |
" The function {converter}.ToInteger(str) takes a string representation of a | |
" base-k number and returns the corresponding positive integer. | |
" Reports an error when the string is an invalid number and returns -1. | |
" | |
" :echomsg g:base26.ToInteger('abacus') | |
" ~ 12815497 | |
" | |
" The function {converter}.Inc(str) takes a string representation of a base-k | |
" number and returns a string with the number incremented by 1. | |
" Reports an error when the string is an invalid number and returns false (0). | |
" | |
" :echomsg g:base26.Inc('abz') | |
" ~ 'aca' | |
" | |
function! numerals#CreateConverter(digitstring) abort | |
if !s:ValidDigitString(a:digitstring) | |
echohl ErrorMsg | |
echomsg "Invalid digit string" | |
echohl None | |
return | |
endif | |
let l:base = len(a:digitstring) | |
let l:digits = range(l:base + 1) | |
let l:digitvals = {} | |
for i in range(l:base) | |
let l:digits[i+1] = a:digitstring[i] | |
let l:digitvals[a:digitstring[i]] = i + 1 | |
endfor | |
let l:context = { | |
\ '_base': l:base, | |
\ '_digits': l:digits, | |
\ '_digitvals': l:digitvals, | |
\ '_validnrre': s:ValidNumberRegexp(a:digitstring), | |
\ 'ValueOf': function('s:ValueOf'), | |
\ 'ToInteger': function('s:ToInteger'), | |
\ 'Inc': function('s:Inc'), | |
\ } | |
return l:context | |
endfunction |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment