1+ using NUnit . Framework ;
2+ using QuestDB . Buffers ;
3+
4+ namespace net_questdb_client_tests ;
5+
6+ public class BufferTests
7+ {
8+ [ Test ]
9+ public void DecimalNegationSimple ( )
10+ {
11+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
12+ // Test simple negative decimal without carry propagation
13+ // -1.0m has unscaled value of -10 (with scale 1)
14+ buffer . Table ( "negation_test" )
15+ . Symbol ( "tag" , "simple" )
16+ . Column ( "dec_neg_one" , - 1.0m )
17+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
18+
19+ // -10 in two's complement: 0xF6 (since 10 = 0x0A, ~0x0A + 1 = 0xF5 + 1 = 0xF6)
20+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_neg_one" , 1 , new byte [ ]
21+ {
22+ 0xF6 ,
23+ } ) ;
24+ }
25+
26+ [ Test ]
27+ public void DecimalNegationCarryLowToMid ( )
28+ {
29+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
30+
31+ // Test carry propagation from low to mid part
32+ // Decimal with low=0x00000001, mid=0x00000000, high=0x00000000
33+ // After negation: low becomes 0xFFFFFFFF (overflow with carry), mid gets carry
34+ // This decimal is: -(2^96 / 10^28 + 1) which causes carry propagation
35+ // Use -4294967296 (which is -2^32) to force carry: low=0x00000000, mid=0x00000001
36+ // After negation: low = ~0 + 1 = 0, mid = ~1 + 1 = 0xFFFFFFFE + 1 = 0xFFFFFFFF
37+ var decimalValue = - 4294967296m ; // -2^32
38+
39+ buffer . Table ( "negation_test" )
40+ . Symbol ( "tag" , "carry_low_mid" )
41+ . Column ( "dec_carry" , decimalValue )
42+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
43+
44+ // -4294967296 has bits: low=0, mid=1, high=0
45+ // Two's complement: low = ~0 + 1 = 0 (with carry), mid = ~1 + carry = 0xFFFFFFFE + 1 = 0xFFFFFFFF
46+ // Result should be: 0xFF, 0x00, 0x00, 0x00, 0x00 (compressed)
47+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_carry" , 0 , new byte [ ]
48+ {
49+ 0xFF ,
50+ 0x00 , 0x00 , 0x00 , 0x00 ,
51+ } ) ;
52+ }
53+
54+ [ Test ]
55+ public void DecimalNegationCarryFullPropagation ( )
56+ {
57+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
58+
59+ // Test carry propagation through all parts (low -> mid -> high)
60+ // Create a decimal where low=0, mid=0, high=1
61+ // This is 2^64 = 18446744073709551616
62+ // After negation: low = ~0 + 1 = 0 (carry), mid = ~0 + 1 = 0 (carry), high = ~1 + 1 = 0xFFFFFFFE + 1 = 0xFFFFFFFF
63+ var decimalValue = - 18446744073709551616m ; // -2^64
64+
65+ buffer . Table ( "negation_test" )
66+ . Symbol ( "tag" , "carry_full" )
67+ . Column ( "dec_full_carry" , decimalValue )
68+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
69+
70+ // -18446744073709551616 has bits: low=0, mid=0, high=1
71+ // Two's complement propagates carry through all parts
72+ // Result: high=0xFFFFFFFF, mid=0, low=0
73+ // In big-endian with sign byte: 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
74+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_full_carry" , 0 , new byte [ ]
75+ {
76+ 0xFF ,
77+ 0x00 , 0x00 , 0x00 , 0x00 ,
78+ 0x00 , 0x00 , 0x00 , 0x00 ,
79+ } ) ;
80+ }
81+
82+ [ Test ]
83+ public void DecimalNegationZeroEdgeCase ( )
84+ {
85+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
86+
87+ // Test that -0.0m is treated as positive zero (line 92 in BufferV3.cs)
88+ // The code checks: var negative = (flags & SignMask) != 0 && value.Value != 0m;
89+ // This ensures that -0.0m doesn't get negated
90+ buffer . Table ( "negation_test" )
91+ . Symbol ( "tag" , "zero" )
92+ . Column ( "dec_zero" , 0.0m )
93+ . Column ( "dec_neg_zero" , - 0.0m )
94+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
95+
96+ // Both 0.0m and -0.0m should be encoded as positive zero: 0x00
97+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_zero" , 1 , new byte [ ]
98+ {
99+ 0x00 ,
100+ } ) ;
101+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_neg_zero" , 1 , new byte [ ]
102+ {
103+ 0x00 ,
104+ } ) ;
105+ }
106+
107+ [ Test ]
108+ public void DecimalNegationSmallestValue ( )
109+ {
110+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
111+
112+ // Test negation of the smallest representable positive value: 0.0000000000000000000000000001m
113+ // This has low=1, mid=0, high=0, scale=28
114+ // After negation: low = ~1 + 1 = 0xFFFFFFFE + 1 = 0xFFFFFFFF
115+ var decimalValue = - 0.0000000000000000000000000001m ;
116+
117+ buffer . Table ( "negation_test" )
118+ . Symbol ( "tag" , "smallest" )
119+ . Column ( "dec_smallest" , decimalValue )
120+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
121+
122+ // Result should be: 0xFF (single byte, compressed)
123+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_smallest" , 28 , new byte [ ]
124+ {
125+ 0xFF ,
126+ } ) ;
127+ }
128+
129+ [ Test ]
130+ public void DecimalNegationWithHighScale ( )
131+ {
132+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
133+
134+ // Test negation with high scale value
135+ // -0.00000001m has scale=8
136+ var decimalValue = - 0.00000001m ; // -10^-8
137+
138+ buffer . Table ( "negation_test" )
139+ . Symbol ( "tag" , "high_scale" )
140+ . Column ( "dec_high_scale" , decimalValue )
141+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
142+
143+ // -1 with scale 8 = 0xFF in two's complement
144+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_high_scale" , 8 , new byte [ ]
145+ {
146+ 0xFF ,
147+ } ) ;
148+ }
149+
150+ [ Test ]
151+ public void DecimalNegationBoundaryCarry ( )
152+ {
153+ var buffer = new BufferV3 ( 128 , 128 , 128 ) ;
154+
155+ // Test a value where low=0xFFFFFFFF (all ones), which after negation becomes 1
156+ // This is the value 4294967295 (2^32 - 1)
157+ // After negation: low = ~0xFFFFFFFF + 1 = 0x00000000 + 1 = 0x00000001
158+ // So -4294967295 should give us: 0xFF, 0xFF, 0xFF, 0xFF, 0x01
159+ var decimalValue = - 4294967295m ;
160+
161+ buffer . Table ( "negation_test" )
162+ . Symbol ( "tag" , "boundary" )
163+ . Column ( "dec_boundary" , decimalValue )
164+ . At ( new DateTime ( 1970 , 01 , 01 , 0 , 0 , 1 ) ) ;
165+
166+ // Two's complement of 4294967295: negates to 0xFFFFFFFF00000001 (represented in big-endian)
167+ DecimalTestHelpers . AssertDecimalField ( buffer . GetSendBuffer ( ) , "dec_boundary" , 0 , new byte [ ]
168+ {
169+ 0xFF ,
170+ 0xFF , 0xFF , 0xFF , 0x01 ,
171+ } ) ;
172+ }
173+
174+ }
0 commit comments