--
--  Test mpq datatype
--

-- Compact output
\t
\a


--
-- mpq input and output functions
--

SELECT '0'::mpq;
SELECT '1'::mpq;
SELECT '-1'::mpq;
SELECT '10'::mpq;
SELECT '-10'::mpq;

SELECT '4294967295'::mpq;   -- limbs boundaries
SELECT '4294967296'::mpq;
SELECT '-4294967296'::mpq;
SELECT '-4294967297'::mpq;
SELECT '18446744073709551614'::mpq;
SELECT '18446744073709551615'::mpq;
SELECT '18446744073709551616'::mpq;
SELECT '18446744073709551617'::mpq;
SELECT '-18446744073709551615'::mpq;
SELECT '-18446744073709551616'::mpq;
SELECT '-18446744073709551617'::mpq;
SELECT '-18446744073709551618'::mpq;
SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq;
SELECT '-12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq;

SELECT '1/4294967295'::mpq;   -- limbs boundaries on denom
SELECT '1/4294967296'::mpq;
SELECT '-1/4294967296'::mpq;
SELECT '-1/4294967297'::mpq;
SELECT '1/18446744073709551614'::mpq;
SELECT '1/18446744073709551615'::mpq;
SELECT '1/18446744073709551616'::mpq;
SELECT '1/18446744073709551617'::mpq;
SELECT '-1/18446744073709551615'::mpq;
SELECT '-1/18446744073709551616'::mpq;
SELECT '-1/18446744073709551617'::mpq;
SELECT '-1/18446744073709551618'::mpq;
SELECT '1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq;
SELECT '-1/12345678901234567890123456789012345678901234567890123456789012345678901234567890'::mpq;

SELECT '1/1'::mpq;
SELECT '2/3'::mpq;
SELECT '640/30'::mpq;
SELECT '-640/30'::mpq;

SELECT '18446744073709551616/18446744073709551616'::mpq;
SELECT '12345678901234567890123456789012345678901234567890123456789012345678901234567890/'
       '88888888888888888888888888888888888888888888888888888888888888888888888888888888'::mpq;

SELECT '1/0'::mpq;

SELECT mpq('1/1');
SELECT mpq('2/3');
SELECT mpq('640/30');
SELECT mpq('-640/30');
SELECT mpq('0xEF/100');
SELECT mpq('0xEF/0x100');

SELECT mpq('10/30', 10);
SELECT mpq('EF/100', 16);
SELECT mpq('0xEF/100', 0);

SELECT mpq('z', 36), mpq('Z', 36);
SELECT mpq('z', 62), mpq('Z', 62);
SELECT mpq('1', 1);
SELECT mpq('1', -10);
SELECT mpq('1', 63);

SELECT text('239'::mpq);
SELECT text('-239'::mpq);
SELECT text('239/256'::mpq);
SELECT text('239'::mpq, 16);
SELECT text('239/256'::mpq, 10);
SELECT text('239/256'::mpq, 16);
SELECT text('239/256'::mpq, 0);
SELECT text('239/256'::mpq, 1);
SELECT text('239/256'::mpq, 2);
SELECT text('239/256'::mpq, 36);
SELECT text('239/256'::mpq, 62);
SELECT text('239/256'::mpq, 63);
SELECT text('239/256'::mpq, -1);
SELECT text('239/256'::mpq, -2);
SELECT text('239/256'::mpq, -36);
SELECT text('239/256'::mpq, -37);


--
-- mpq cast
--

SELECT 0::smallint::mpq, (-32768)::smallint::mpq, 32767::smallint::mpq;
SELECT 0::integer::mpq, (-2147483648)::integer::mpq, 2147483647::integer::mpq;
SELECT 0::bigint::mpq, (-9223372036854775808)::bigint::mpq, 9223372036854775807::bigint::mpq;
SELECT 0::numeric::mpq, (-12345678901234567890)::numeric::mpq, 12345678901234567890::numeric::mpq;
SELECT 0::mpz::mpq, (-12345678901234567890)::mpz::mpq, 12345678901234567890::mpz::mpq;
SELECT 0.0::float4::mpq, (-12345.25)::float4::mpq, 12345.25::float4::mpq;
SELECT 0.0::float8::mpq, (-123456789012.25)::float8::mpq, 123456789012.25::float8::mpq;
SELECT 0.1::float4::mpq;    -- don't know if it's portable
SELECT 0.1::float8::mpq;
SELECT 0.0::numeric::mpq, (-1234567890.12345)::numeric::mpq, 1234567890.12345::numeric::mpq;

SELECT 0::mpq, 1::mpq, (-1)::mpq;       -- automatic casts
SELECT 1000000::mpq, (-1000000)::mpq;
SELECT 1000000000::mpq, (-1000000000)::mpq;
SELECT 1000000000000000::mpq, (-1000000000000000)::mpq;
SELECT 1000000000000000000000000000000::mpq, (-1000000000000000000000000000000)::mpq;
SELECT 0.0::mpq, (-1234567890.12345)::mpq, 1234567890.12345::mpq;
SELECT 'NaN'::decimal::mpq;

SELECT -1::mpq;       -- these take the unary minus to work
SELECT -1000000::mpq;
SELECT -1000000000::mpq;
SELECT -1000000000000000::mpq;
SELECT -1000000000000000000000000000000::mpq;

SELECT 123.10::mpq::mpz, (-123.10)::mpq::mpz;
SELECT 123.90::mpq::mpz, (-123.90)::mpq::mpz;
SELECT 123.10::mpq::int2, (-123.10)::mpq::int2;
SELECT 123.10::mpq::int4, (-123.10)::mpq::int4;
SELECT 123.10::mpq::int8, (-123.10)::mpq::int8;
SELECT 32767::mpq::int2;
SELECT 32768::mpq::int2;
SELECT (-32768)::mpq::int2;
SELECT (-32769)::mpq::int2;
SELECT 2147483647::mpq::int4;
SELECT 2147483648::mpq::int4;
SELECT (-2147483648)::mpq::int4;
SELECT (-2147483649)::mpq::int4;
SELECT 9223372036854775807::mpq::int8;
SELECT 9223372036854775808::mpq::int8;
SELECT (-9223372036854775808)::mpq::int8;
SELECT (-9223372036854775809)::mpq::int8;
SELECT 123.10::mpq::float4, (-123.10)::mpq::float4;
SELECT 123.10::mpq::float8, (-123.10)::mpq::float8;
SELECT pow(10::mpz,400)::mpq::float4;       -- +inf
SELECT (-pow(10::mpz,400))::mpq::float4;    -- -inf
SELECT mpq(1,pow(10::mpz,400))::float4;     -- underflow
SELECT pow(10::mpz,400)::mpq::float8;
SELECT (-pow(10::mpz,400))::mpq::float8;
SELECT mpq(1,pow(10::mpz,400))::float8;
SELECT 1::mpq::numeric;
SELECT 123.456::mpq::numeric;
SELECT 123.456::mpq::numeric(10);
SELECT 123.456::mpq::numeric(10,2);
SELECT mpq(4,3)::numeric;
SELECT mpq(4,3)::numeric(10);
SELECT mpq(4,3)::numeric(10,5);
SELECT mpq(40000,3)::numeric(10,5);
SELECT mpq(-40000,3)::numeric(10,5);
SELECT mpq(400000,3)::numeric(10,5);

-- function-style casts
SELECT mpq('0'::varchar);
SELECT mpq('0'::int2);
SELECT mpq('0'::int4);
SELECT mpq('0'::int8);
SELECT mpq('0'::float4);
SELECT mpq('0'::float8);
SELECT mpq('0'::numeric);
SELECT mpq('0'::mpz);

SELECT text(0::mpq);
SELECT int2(0::mpq);
SELECT int4(0::mpq);
SELECT int8(0::mpq);
SELECT float4(0::mpq);
SELECT float8(0::mpq);
SELECT mpz('0'::mpq);

-- tricky cases of cast to numeric
select (x::mpz::mpq / 100)::decimal      from generate_series(-2, 2) x;
select (x::mpz::mpq / 100)::decimal(6,0) from generate_series(-2, 2) x;
select (x::mpz::mpq / 100)::decimal(6,1) from generate_series(-2, 2) x;
select (x::mpz::mpq / 100)::decimal(6,2) from generate_series(-2, 2) x;

SELECT mpq(10, 4), mpq(10, -4);
SELECT mpq(10, 0);
-- fails if mpq(int, int) or similar are availiable
SELECT mpq(4000000000000000000,3);
-- TODO: this shoud work.
-- currently not accepting it for ambiguous type promotion problems,
-- but this could change in the future if we find how to fix the above problem
SELECT mpq(47563485764385764395874365986384, 874539847539845639485769837553465);
-- Enable these checks if the above is solved.
-- SELECT mpq(1230::numeric, 123::numeric);
-- SELECT mpq(123.45::numeric, 1::numeric);
-- SELECT mpq(1::numeric, 123.45::numeric);
-- SELECT mpq(123::numeric, 0::numeric);
SELECT mpq(47563485764385764395874365986384::mpz, 874539847539845639485769837553465::mpz);
SELECT mpq('10'::mpz, '0'::mpz);
SELECT num('4/5'::mpq);
SELECT den('4/5'::mpq);


--
-- mpq arithmetic
--

SELECT -('0'::mpq), +('0'::mpq), -('1'::mpq), +('1'::mpq), -('-1'::mpq), +('-1'::mpq);
SELECT -('1234567890123456/7890'::mpq), +('1234567890123456/7890'::mpq);
SELECT '4/5'::mpq + '6/8'::mpq;
SELECT '4/5'::mpq - '6/8'::mpq;
SELECT '4/5'::mpq * '6/8'::mpq;
SELECT '4/5'::mpq / '6/8'::mpq;
SELECT '4/5'::mpq / '0'::mpq;

SELECT '4/5'::mpq << 4;
SELECT '4/5'::mpq << -1;
SELECT '4/5'::mpq >> 4;
SELECT '4/5'::mpq >> -1;


--
-- mpq unary function
--
SELECT abs(mpq(1,3));
SELECT abs(mpq(-1,3));
SELECT abs(mpq(1,-3));
SELECT abs(mpq(-1,-3));
SELECT inv(mpq(1,3));
SELECT inv(mpq(-1,3));
SELECT inv(mpq(3,1));
SELECT inv(mpq(-3,1));
SELECT inv(0::mpq);

SELECT limit_den(3.141592653589793, 10);
SELECT limit_den(3.141592653589793, 100);
SELECT limit_den(3.141592653589793, 1000000);
SELECT limit_den(3.141592653589793);
SELECT limit_den('4321/8765', 10000);
SELECT limit_den(3.141592653589793, 10000);
SELECT limit_den(-3.141592653589793, 10000);
SELECT limit_den(3.141592653589793, 113);
SELECT limit_den(3.141592653589793, 112);
SELECT limit_den('201/200', 100);
SELECT limit_den('201/200', 101);
SELECT limit_den(0, 10000);


--
-- mpq ordering operators
--

select 1000::mpq =   999::mpq;
select 1000::mpq =  1000::mpq;
select 1000::mpq =  1001::mpq;
select 1000::mpq <>  999::mpq;
select 1000::mpq <> 1000::mpq;
select 1000::mpq <> 1001::mpq;
select 1000::mpq !=  999::mpq;
select 1000::mpq != 1000::mpq;
select 1000::mpq != 1001::mpq;
select 1000::mpq <   999::mpq;
select 1000::mpq <  1000::mpq;
select 1000::mpq <  1001::mpq;
select 1000::mpq <=  999::mpq;
select 1000::mpq <= 1000::mpq;
select 1000::mpq <= 1001::mpq;
select 1000::mpq >   999::mpq;
select 1000::mpq >  1000::mpq;
select 1000::mpq >  1001::mpq;
select 1000::mpq >=  999::mpq;
select 1000::mpq >= 1000::mpq;
select 1000::mpq >= 1001::mpq;

select mpq_cmp(1000::mpq,  999::mpq);
select mpq_cmp(1000::mpq, 1000::mpq);
select mpq_cmp(1000::mpq, 1001::mpq);

-- Can create btree and hash indexes
create table test_mpq_idx (q mpq);
insert into test_mpq_idx select generate_series(1, 10000);
create index test_mpq_btree_idx on test_mpq_idx using btree (q);
create index test_mpq_hash_idx on test_mpq_idx using hash (q);

-- Hash is compatible with mpz
select mpq_hash(0) = mpz_hash(0);
select mpq_hash(1000) = mpz_hash(1000);
select mpq_hash(-1000) = mpz_hash(-1000);
select mpq_hash('123456789012345678901234567890123456789012345678901234567890')
     = mpz_hash('123456789012345678901234567890123456789012345678901234567890');

-- den is used in hash
select mpq_hash(2) <> mpq_hash('2/3');
select mpq_hash('2/3') <> mpq_hash('2/5');


--
-- mpq aggregation
--

CREATE TABLE mpqagg(q mpq);

SELECT sum(q) FROM mpqagg;      -- NULL sum

INSERT INTO mpqagg SELECT mpq(x+1, x) from generate_series(1, 100) x;
INSERT INTO mpqagg VALUES (NULL);

SELECT sum(q) FROM mpqagg;
SELECT prod(q) FROM mpqagg;
SELECT min(q) FROM mpqagg;
SELECT max(q) FROM mpqagg;

-- check correct values when the sortop kicks in
CREATE INDEX mpqagg_idx ON mpqagg(q);
SELECT min(q) FROM mpqagg;
SELECT max(q) FROM mpqagg;

-- check aggregates work in windows functions too
CREATE TABLE test_mpq_win(q mpq);
INSERT INTO test_mpq_win SELECT mpq(1::mpz, i::mpz) from generate_series(1,500) i;
SELECT DISTINCT den(q) % 5, prod(q) OVER (PARTITION BY den(q) % 5) FROM test_mpq_win ORDER BY 1;
