Wednesday, June 22, 2005

MIT HAKMEM Bit Count in Oracle SQL

This is not about performance... just to illustrate various approaches I've considered while reading this. Interestingly, that even the question was about PL/SQL implementation everyone tried to do it in SQL. Anyway, here is my (Update 06/25/2005: one buddy there was quite close to implement the second one) ideas about it (sure, mit hakmem is borrowed):
-- Let's employ regexp and replace
-- the hex values by their count
SELECT n
, NVL(
LENGTH(
REGEXP_REPLACE(
TO_CHAR(n, 'FMXXXXXXXX')
, '([1248]*)([3569AC]*)([7BDE]*)([F]*)[0]*'
, '\1\2\2\3\3\3\4\4\4\4')
)
, 0
) cnt
FROM tbl
/
-- [([1248]*) and \1 can be removed because
-- it is 1 to 1 character replacement, so
-- check this out
SELECT REGEXP_REPLACE(
'8F13247F7018'
, '([3569AC]*)([7BDE]*)([F]*)[0]*'
, '\1\1\2\2\2\3\3\3\3') cnt
FROM dual
/
-- This uses a dynamically built table and
-- replaces hex values by count of bits, as
-- you can see the principle is the same as
-- above in regexp implementation
-- Update 06/25/2005:
-- The code was changed a bit it's Ok not to
-- replace 1,2,4,8 and leave them as is.
SELECT NVL(
LENGTH(
REPLACE(REPLACE(REPLACE(
TRANSLATE(TO_CHAR(n, 'FMXXXXXXXX')
, '35679ABCDEF0'
, '...,..,.,,:'
/* '22232232334' */
)
, '.', '11'), ',', '111'), ':', '1111')
)
, 0
)
FROM tbl
/
-- Finally, hakmem...
SELECT n
, MOD(
BITAND(
(n - BITAND(n / 2, 3681400539) - BITAND(n / 4, 1227133513))
+ (n - BITAND(n / 2, 3681400539) - BITAND(n / 4, 1227133513)) / 8
, 3340530119
)
, 63
) cnt
FROM tbl
WHERE n BETWEEN 0 AND 4294967295
/
Implemented in PL/SQL hakmem is quite fast. On 10gR2 it's even faster. :-) You can read more about hakmem here.

Update 06/24/2005
: Here you can find more about bitcount implementations in C.

Update 08/08/2005
: Bit Twiddling Hacks

2 comments:

Anonymous said...

'bout replace algorithm - where is (c) sign? :-)

WBR, RA\/EN from sql.ru.
22.06.2005...

JFF

Anonymous said...

Oops :-)
Haven't read from beginning.

WBR, "NO-BUDDY" :(