Reduce length of the pseudo-encrypt function ouput - postgresql
i have a question about the pseudo-encrypt function for postgres.
Is there any way that I can reduce the output to 6? I really like this function and want to use it, but only need a output between 1 and 999999.
This question is related to my last question. I want to use it to created unqiue numbers between 1 and 999999.
Thank you.
Use mod on the generated value to generate number in range from start_value to end_value:
select start_value + mod(pseudo_encrypt(number), end_value - start_value + 1);
For your case this will be look like:
select 1 + mod(pseudo_encrypt(23452), 999999);
It's not quite straightforward to set an upper bound of 999999, as the algorithm operates on blocks of bits, so it's hard to get away from powers of two.
You can work around this by cycle walking - just try encrypt(n), encrypt(encrypt(n)), encrypt(encrypt(encrypt(n)))... until you end up with a result in the range [1,999999]. In the interest of keeping the number of iterations to a minimum, you want to adjust the block size to get you as close to this range as possible.
This version will let you specify a range for the input/output:
CREATE OR REPLACE FUNCTION pseudo_encrypt(
value INT8,
min INT8 DEFAULT 0,
max INT8 DEFAULT (2^62::NUMERIC)-1
) RETURNS INT8 AS
$$
DECLARE
rounds CONSTANT INT = 3;
L INT8[];
R INT8[];
i INT;
blocksize INT;
blockmask INT8;
result INT8;
BEGIN
max = max - min;
value = value - min;
IF NOT ((value BETWEEN 0 AND max) AND (max BETWEEN 0 AND 2^62::NUMERIC-1)) THEN
RAISE 'Input out of range';
END IF;
blocksize = ceil(char_length(ltrim(max::BIT(64)::TEXT,'0'))/2.0);
blockmask = (2^blocksize::NUMERIC-1)::INT8;
result = value;
LOOP
L[1] = (result >> blocksize) & blockmask;
R[1] = result & blockmask;
FOR i IN 1..rounds LOOP
L[i+1] = R[i];
R[i+1] = L[i] # ((941083981*R[i] + 768614336404564651) & blockmask);
END LOOP;
result = (L[rounds]::INT8 << blocksize) | R[rounds];
IF result <= max THEN
RETURN result + min;
END IF;
END LOOP;
END;
$$
LANGUAGE plpgsql STRICT IMMUTABLE;
I can't guarantee its correctness, but you can easily show that it maps [1,999999] back to [1,999999]:
SELECT i FROM generate_series(1,999999) s(i)
EXCEPT
SELECT pseudo_encrypt(i,1,999999) FROM generate_series(1,999999) s(i)
Related
Function to return N significant digits of given number
I have seen several questions on this topic, but the answers do not seem useful to me. I must create a function in PostgreSQL 10.5 that returns a number with N significant digits. I have tried different solutions for this problem, however I have a problem with a particular case. Below the example, where nmNumber is the number of the input parameters and nmSf is the number of significant digits. SELECT round(nmNumber * power(10, nmSf-1-floor(log(abs(nmNumber ))))) / power(10, nmSf-1-floor(log(abs(nmNumber)))) result1, round(nmNumber, cast(-floor(log(abs(nmNumber))) as integer)) result2, floor(nmNumber / (10 ^ floor(log(nmNumber) - nmSf + 1))) * (10 ^ floor(log(nmNumber) - nmSf + 1)) result3; If nmNumber = 0.0801 and nmSf = 2 then: result 1 = 0.08; result2 = 0.08; result 3 = 0.08 The three results are incorrect given that: The zeros immediately after the decimal point are not significant digits. All non-zero digits are significant. The zeros after digits other than zero in a decimal are significant. According to point 3, the correct result of the previous example is: 0.080 and not 0.08 and although mathematically it turns out to be the same, visually I must obtain this result. Some idea of how I can solve my problem? I suppose that returning a VARCHAR in exchange for a NUMERIC is part of the solution to be proposed. Some idea or I'm missing something. Thank you.
This would implement what you ask for: CREATE OR REPLACE FUNCTION f_significant_nr(_nr numeric, _sf int, OUT _nr1 text) AS $func$ DECLARE _sign bool; -- record negative sign _int int; -- length of integral part _nr_text text; -- intermediate text state BEGIN IF _sf < 1 THEN RAISE EXCEPTION '_sf must be > 0!'; ELSIF _nr IS NULL THEN RETURN; -- returns NULL ELSIF _nr = 0 THEN _nr1 := '0'; RETURN; ELSIF _sf >= length(translate(_nr::text, '-.','')) THEN -- not enough digits, optional shortcut _nr1 := _nr; RETURN; ELSIF abs(_nr) < 1 THEN _nr1 := COALESCE(substring(_nr::text, '^.*?[1-9]\d{' || _sf-1 || '}'), _nr::text); RETURN; ELSIF _nr < 0 THEN -- extract neg sign _sign := true; _nr := _nr * -1; END IF; _int := trunc(log(_nr))::int + 1; -- <= 0 was excluded above! IF _sf < _int THEN -- fractional digits not touched _nr1 := rpad(left(_nr::text, _sf), _int, '0'); ELSE _nr1 := trunc(_nr)::text; IF _sf > _int AND (_nr % 1) > 0 THEN -- _sf > _int and we have significant fractional digits _nr_text := right((_nr % 1)::text, -1); -- remainder: ".123" _nr1 := _nr1 || COALESCE(substring(_nr_text, '^.*?[1-9]\d{' || (_sf - _int - 1)::text || '}'), _nr_text); END IF; END IF; IF _sign THEN _nr1 := '-' || _nr1; END IF; END $func$ LANGUAGE plpgsql; db<>fiddle here - with test case. Deals with everything you throw at it, incl NULL, 0, 0.000 or negative numbers. But I have doubts about your point 3 as commented.
postgres knows how to do that (even point 3), you just need to ask it the right way. create or replace function sig_fig(num numeric,prec int) returns numeric language sql as $$ select to_char($1, '9.'||repeat('9',$2-1)||'EEEE')::numeric $$; That creates a format string producing scientific notation with a 3 digit mantissa. Uses that that to get the number as text Then converts that back to numeric yielding a conventional number with that many significant figures. demo: with v as ( values (3.14159),(1234567),(0.02),(1024),(42),(0.0098765),(-6666) ) select column1 ,sig_fig(column1,3) as "3sf" from v; column1 | 3sf -----------+--------- 3.14159 | 3.14 1234567 | 1230000 0.02 | 0.0200 1024 | 1020 42 | 42.0 0.0098765 | 0.00988 -6666 | -6670 (7 rows)
Error declaring variable & if-statement
I am trying to write a stored procedure that uses if statements and variables however I keep getting a SQL0104N error. It seems to have difficulty with the declaring of the variables. I have also tried substituting the 'v' prefix with a '#' to denote local variable but it doesn't seem to help. error says unxpected token 'DECLARE vCUBE DOUBLE' Any insight would be very helpful. Code is listed below: CREATE PROCEDURE DIMENSION_CALC( IN iORID INTEGER, IN iPIECES DOUBLE, IN iLENGTH_1 DOUBLE, IN iLENGTH_UNITS DOUBLE, IN iWIDTH DOUBLE, IN iWIDTH_UNITS DOUBLE, IN iHEIGHT DOUBLE, IN iHEIGHT_UNITS DOUBLE, IN iCUBE DOUBLE, IN iCUBE_UNITS DOUBLE ) LANGUAGE SQL BEGIN DECLARE vCUBE DOUBLE; DECLARE vLINEAR DOUBLE; SET vCUBE = iPIECES * GET_CUBE(iCUBE, iCUBE_UNITS, iHEIGHT, iHEIGHT_UNITS, iWIDTH, iWIDTH_UNITS, iLENGTH_1, iLENGTH_UNITS); IF CALC_LENGTH(iWIDTH_UNITS, iWIDTH) * iPIECES <= 4 THEN SET vLINEAR = MULTIPLIER_LINEAR_FEET(4) * CALC_LENGTH(iLENGTH_UNITS,iLENGTH); ELSEIF CALC_LENGTH(iWIDTH_UNITS, iWIDTH)*iPIECES > 4 AND CALC_LENGTH(iWIDTH_UNITS, iWIDTH)<= 4 THEN SET vLINEAR = MULTIPLIER_LINEAR_FEET(CALC_LENGTH(iWIDTH_UNITS, iWIDTH)*iPIECES) * CALC_LENGTH(iLENGTH_UNITS,iLENGTH_1); ELSEIF CALC_LENGTH(iWIDTH_UNITS, iWIDTH) > 4 THEN SET vLINEAR = iPIECES * CALC_LENGTH(iLENGTH_UNITS,iLENGTH_1); END IF; IF TOTAL_LINEAR(iORID) >= 10 THEN UPDATE TLDTL SET CUBE = vLINEAR * 100, WHERE ORDER_ID = iORID; ELSEIF TOTAL_LINEAR(iORID) < 10 THEN UPDATE TLDTL SET CUBE = vCUBE WHERE ORDER_ID = iORID; END IF; END
Got "Boolean" expected "LongInt" pascal
I get this error on my insertion sort algorithm: insertionsort.lpr(19,17) Error: Incompatible types: got "Boolean" expected "LongInt" Here's the line 19 of my code while j > 0 and A[j]>key do I have tried googling all over the internet but i couldn't find any syntax errors or anything. Here's the full code if it helps : program instert; uses crt; const N = 5; var i:integer; j:integer; key:integer; A : Array[1..N] of Integer; procedure insertionsort; begin for i := 2 to N do begin key := A[1]; j:= i - 1; while j > 0 and A[j]>key do begin A[j+1] := A[j] ; j := j-1; end; A[j+1] := key ; end; end; begin A[1]:= 9; A[2]:= 6; A[3]:= 7; A[4]:= 1; A[5]:= 2; insertionsort; end. I also get the same error on the bubble sort algorithm i did. Here's the error line bubblesort.lpr(26,14) Error: Incompatible types: got "Boolean" expected "LongInt" Here's line 26 of my algorithm: until flag = false or N = 1 ; Here's the full code: program bubblesort; uses crt; var flag:boolean; count:integer; temp:integer; N:integer; A : Array[1..N] of Integer; procedure bubblesort ; begin Repeat flag:=false; for count:=1 to (N-1) do begin if A[count] > A[count + 1] then begin temp := A[count]; A[count] := A[count + 1]; A[count] := temp; flag := true; end; end; N := N - 1; until flag = false or N = 1 ; end; begin A[1]:= 9; A[2]:= 6; A[3]:= 7; A[4]:= 1; A[5]:= 2; N := 5; bubblesort; end.
In Pascal, boolean operators and and or have higher precedence than the comparison operators >, =, etc. So in the expression: while j > 0 and A[j] > key do Given that and has higher precedence, Pascal sees this as: while (j > (0 and A[j])) > key do 0 and A[j] are evaluated as a bitwise and (since the arguments are integers) resulting in an integer. Then the comparison, j > (0 and A[j]) is evaluated as a boolean result, leaving a check of that with > key, which is boolean > longint. You then get the error that a longint is expected instead of the boolean for the arithmetic comparison. The way to fix it is to parenthesize: while (j > 0) and (A[j] > key) do ... The same issue applies with this statement: until flag = false or N = 1 ; which yields an error because or is higher precedence than =. So you can parenthesize: until (flag = false) or (N = 1); Or, more canonical for booleans: until not flag or (N = 1); // NOTE: 'not' is higher precedence than 'or' When in doubt about the precedence of operators, parenthesizing is a good idea, as it removes the doubt about what order operations are going to occur.
CRC32 function with PL/pgSQL
How to compute a 32 bits cyclic redundancy check (CRC-32) as a function in PostgreSQL, the same way as MySQL?
You can create the function yourself, this is a working example for PostgreSQL 9.6 CREATE OR REPLACE FUNCTION crc32(text_string text) RETURNS bigint AS $$ DECLARE tmp bigint; i int; j int; byte_length int; binary_string bytea; BEGIN IF text_string = '' THEN RETURN 0; END IF; i = 0; tmp = 4294967295; byte_length = bit_length(text_string) / 8; binary_string = decode(replace(text_string, E'\\\\', E'\\\\\\\\'), 'escape'); LOOP tmp = (tmp # get_byte(binary_string, i))::bigint; i = i + 1; j = 0; LOOP tmp = ((tmp >> 1) # (3988292384 * (tmp & 1)))::bigint; j = j + 1; IF j >= 8 THEN EXIT; END IF; END LOOP; IF i >= byte_length THEN EXIT; END IF; END LOOP; RETURN (tmp # 4294967295); END $$ IMMUTABLE LANGUAGE plpgsql; Inspired from an old post with an non crc32 accepted answer. I couldn't find the original code from thinking sphinx.
create it in PG11. text to crc32(for Little-endian)。 result is equal to java.util.zip.CRC32. if you want crc32(t text), you can select crc32_update(0, t) create or replace function crc32_update(crc bigint, t text) returns bigint as $$ declare bytes bytea; byte bigint; len bigint; long_mask bigint = 4294967295; -- 0xFFFFFFFF byte_mask integer = 255; -- 0xFF rt8_mask bigint = 16777215; -- 0xFF000000 crc_table bigint[] = array[ 0, 1996959894, -301047508, -1727442502, 124634137, 1886057615, -379345611, -1637575261, 249268274, 2044508324, -522852066, -1747789432, 162941995, 2125561021, -407360249, -1866523247, 498536548, 1789927666, -205950648, -2067906082, 450548861, 1843258603, -187386543, -2083289657, 325883990, 1684777152, -43845254, -1973040660, 335633487, 1661365465, -99664541, -1928851979, 997073096, 1281953886, -715111964, -1570279054, 1006888145, 1258607687, -770865667, -1526024853, 901097722, 1119000684, -608450090, -1396901568, 853044451, 1172266101, -589951537, -1412350631, 651767980, 1373503546, -925412992, -1076862698, 565507253, 1454621731, -809855591, -1195530993, 671266974, 1594198024, -972236366, -1324619484, 795835527, 1483230225, -1050600021, -1234817731, 1994146192, 31158534, -1731059524, -271249366, 1907459465, 112637215, -1614814043, -390540237, 2013776290, 251722036, -1777751922, -519137256, 2137656763, 141376813, -1855689577, -429695999, 1802195444, 476864866, -2056965928, -228458418, 1812370925, 453092731, -2113342271, -183516073, 1706088902, 314042704, -1950435094, -54949764, 1658658271, 366619977, -1932296973, -69972891, 1303535960, 984961486, -1547960204, -725929758, 1256170817, 1037604311, -1529756563, -740887301, 1131014506, 879679996, -1385723834, -631195440, 1141124467, 855842277, -1442165665, -586318647, 1342533948, 654459306, -1106571248, -921952122, 1466479909, 544179635, -1184443383, -832445281, 1591671054, 702138776, -1328506846, -942167884, 1504918807, 783551873, -1212326853, -1061524307, -306674912, -1698712650, 62317068, 1957810842, -355121351, -1647151185, 81470997, 1943803523, -480048366, -1805370492, 225274430, 2053790376, -468791541, -1828061283, 167816743, 2097651377, -267414716, -2029476910, 503444072, 1762050814, -144550051, -2140837941, 426522225, 1852507879, -19653770, -1982649376, 282753626, 1742555852, -105259153, -1900089351, 397917763, 1622183637, -690576408, -1580100738, 953729732, 1340076626, -776247311, -1497606297, 1068828381, 1219638859, -670225446, -1358292148, 906185462, 1090812512, -547295293, -1469587627, 829329135, 1181335161, -882789492, -1134132454, 628085408, 1382605366, -871598187, -1156888829, 570562233, 1426400815, -977650754, -1296233688, 733239954, 1555261956, -1026031705, -1244606671, 752459403, 1541320221, -1687895376, -328994266, 1969922972, 40735498, -1677130071, -351390145, 1913087877, 83908371, -1782625662, -491226604, 2075208622, 213261112, -1831694693, -438977011, 2094854071, 198958881, -2032938284, -237706686, 1759359992, 534414190, -2118248755, -155638181, 1873836001, 414664567, -2012718362, -15766928, 1711684554, 285281116, -1889165569, -127750551, 1634467795, 376229701, -1609899400, -686959890, 1308918612, 956543938, -1486412191, -799009033, 1231636301, 1047427035, -1362007478, -640263460, 1088359270, 936918000, -1447252397, -558129467, 1202900863, 817233897, -1111625188, -893730166, 1404277552, 615818150, -1160759803, -841546093, 1423857449, 601450431, -1285129682, -1000256840, 1567103746, 711928724, -1274298825, -1022587231, 1510334235, 755167117 ]; begin crc = ~crc; len = bit_length(t) / 8; bytes = decode(t, 'escape'); for i in 0..len-1 loop byte = (get_byte(bytes, i))::bigint; crc = (crc >> 8 & rt8_mask) # crc_table[((crc # byte) & byte_mask) + 1]; end loop; crc = ~crc; return crc & long_mask; end $$ immutable language plpgsql
If you want a checksum of the results which does not need to be crc32, you can use md5: select sum(('x' || lpad(substring(md5(mycolumn), 27, 6), 8, '0'))::bit(32)::int) from mytable md5 returns hex (128 bits). I use the final 6 characters of the md5 so it does not overflow an int. If the sum overflows an int, it may error. This should give a rudimentary checksum of the results. SELECT 'xffffffff'::bit(32)::int; > -1 SELECT 'x7fffffff'::bit(32)::int + 1; ERROR: integer out of range SELECT 'x7fffffffffffffff'::bit(64)::bigint + 1; ERROR: bigint out of range
How to convert integer to hex notation?
Using: Firebird 2.5.3 In a stored procedure (PSQL), converting a number from hex notation to decimal notation is done easily: DECLARE VARIABLE I INTEGER; BEGIN I = CAST('0x0FFFE' AS INTEGER); -- I will have the value 65534 How can the reverse be achieved? ie. Convert from decimal notation to hex notation?
Short of using a UDF (which would mean using an external library file), the solution is to write a stored procedure to accomplish this: SET TERM ^^ ; CREATE PROCEDURE INTTOHEX ( INPUTNUMBER BigInt) returns ( OUTPUTNUMBER VarChar(8)) AS DECLARE VARIABLE Q BigInt; DECLARE VARIABLE R BigInt; DECLARE VARIABLE T BigInt; DECLARE VARIABLE H VARCHAR(1); DECLARE VARIABLE S VARCHAR(6); begin /* Max input value allowed is: 4294967295 */ S = 'ABCDEF'; Q = 1; OUTPUTNUMBER = ''; T = INPUTNUMBER; WHILE (Q <> 0) DO BEGIN Q = T / 16; R = MOD(T, 16); T = Q; IF (R > 9) THEN H = SUBSTRING(S FROM (R-9) FOR 1); ELSE H = R; OUTPUTNUMBER = H || OUTPUTNUMBER ; END SUSPEND; end ^^ SET TERM ; ^^ You can call this stored procedure from standard SQL or another stored procedure like this: For example: SELECT OUTPUTNUMBER FROM INTTOHEX(65534);
just did a short firebird function, could handle bigger number, maybe could help someone CREATE OR ALTER FUNCTION INT64TOHEX ( C BIGINT) RETURNS VARCHAR(32) AS DECLARE VARIABLE IREM INTEGER; DECLARE VARIABLE HEX VARCHAR(32); BEGIN IREM = MOD(C, 0XFF); HEX = ''; WHILE (IREM > 0) DO BEGIN HEX = SUBSTRING('0123456789abcdef' FROM BIN_SHR(BIN_AND(C, 0XFF), 4) + 1 FOR 1) || SUBSTRING('0123456789abcdef' FROM BIN_AND(BIN_AND(C, 0XFF), 15) + 1 FOR 1) || HEX; C = BIN_SHR(C, 8); IREM = MOD(C, 0XFF); END RETURN TRIM(HEX); END