import
math
from
bisect
import
bisect_left, bisect_right
from
typing
import
Generic, Iterable, Iterator, TypeVar, Union,
List
T
=
TypeVar(
'T'
)
class
SortedSet(Generic[T]):
BUCKET_RATIO
=
50
REBUILD_RATIO
=
170
def
_build(
self
, a
=
None
)
-
>
None
:
"Evenly divide `a` into buckets."
if
a
is
None
: a
=
list
(
self
)
size
=
self
.size
=
len
(a)
bucket_size
=
int
(math.ceil(math.sqrt(size
/
self
.BUCKET_RATIO)))
self
.a
=
[a[size
*
i
/
/
bucket_size: size
*
(i
+
1
)
/
/
bucket_size]
for
i
in
range
(bucket_size)]
def
__init__(
self
, a: Iterable[T]
=
[])
-
>
None
:
"Make a new SortedSet from iterable. / O(N) if sorted and unique / O(N log N)"
a
=
list
(a)
if
not
all
(a[i] < a[i
+
1
]
for
i
in
range
(
len
(a)
-
1
)):
a
=
sorted
(
set
(a))
self
._build(a)
def
__iter__(
self
)
-
> Iterator[T]:
for
i
in
self
.a:
for
j
in
i:
yield
j
def
__reversed__(
self
)
-
> Iterator[T]:
for
i
in
reversed
(
self
.a):
for
j
in
reversed
(i):
yield
j
def
__len__(
self
)
-
>
int
:
return
self
.size
def
__repr__(
self
)
-
>
str
:
return
"SortedSet"
+
str
(
self
.a)
def
__str__(
self
)
-
>
str
:
s
=
str
(
list
(
self
))
return
"{"
+
s[
1
:
len
(s)
-
1
]
+
"}"
def
_find_bucket(
self
, x: T)
-
>
List
[T]:
"Find the bucket which should contain x. self must not be empty."
for
a
in
self
.a:
if
x <
=
a[
-
1
]:
return
a
return
a
def
__contains__(
self
, x: T)
-
>
bool
:
if
self
.size
=
=
0
:
return
False
a
=
self
._find_bucket(x)
i
=
bisect_left(a, x)
return
i !
=
len
(a)
and
a[i]
=
=
x
def
add(
self
, x: T)
-
>
bool
:
"Add an element and return True if added. / O(√N)"
if
self
.size
=
=
0
:
self
.a
=
[[x]]
self
.size
=
1
return
True
a
=
self
._find_bucket(x)
i
=
bisect_left(a, x)
if
i !
=
len
(a)
and
a[i]
=
=
x:
return
False
a.insert(i, x)
self
.size
+
=
1
if
len
(a) >
len
(
self
.a)
*
self
.REBUILD_RATIO:
self
._build()
return
True
def
discard(
self
, x: T)
-
>
bool
:
"Remove an element and return True if removed. / O(√N)"
if
self
.size
=
=
0
:
return
False
a
=
self
._find_bucket(x)
i
=
bisect_left(a, x)
if
i
=
=
len
(a)
or
a[i] !
=
x:
return
False
a.pop(i)
self
.size
-
=
1
if
len
(a)
=
=
0
:
self
._build()
return
True
def
lt(
self
, x: T)
-
> Union[T,
None
]:
"Find the largest element < x, or None if it doesn't exist."
for
a
in
reversed
(
self
.a):
if
a[
0
] < x:
return
a[bisect_left(a, x)
-
1
]
def
le(
self
, x: T)
-
> Union[T,
None
]:
"Find the largest element <= x, or None if it doesn't exist."
for
a
in
reversed
(
self
.a):
if
a[
0
] <
=
x:
return
a[bisect_right(a, x)
-
1
]
def
gt(
self
, x: T)
-
> Union[T,
None
]:
"Find the smallest element > x, or None if it doesn't exist."
for
a
in
self
.a:
if
a[
-
1
] > x:
return
a[bisect_right(a, x)]
def
ge(
self
, x: T)
-
> Union[T,
None
]:
"Find the smallest element >= x, or None if it doesn't exist."
for
a
in
self
.a:
if
a[
-
1
] >
=
x:
return
a[bisect_left(a, x)]
def
__getitem__(
self
, x:
int
)
-
> T:
"Return the x-th element, or IndexError if it doesn't exist."
if
x <
0
: x
+
=
self
.size
if
x <
0
:
raise
IndexError
for
a
in
self
.a:
if
x <
len
(a):
return
a[x]
x
-
=
len
(a)
raise
IndexError
def
index(
self
, x: T)
-
>
int
:
"Count the number of elements < x."
ans
=
0
for
a
in
self
.a:
if
a[
-
1
] >
=
x:
return
ans
+
bisect_left(a, x)
ans
+
=
len
(a)
return
ans
def
index_right(
self
, x: T)
-
>
int
:
"Count the number of elements <= x."
ans
=
0
for
a
in
self
.a:
if
a[
-
1
] > x:
return
ans
+
bisect_right(a, x)
ans
+
=
len
(a)
return
ans
def
ccw(ax, ay, bx, by, cx, cy):
dx1
=
bx
-
ax
dy1
=
by
-
ay
dx2
=
cx
-
ax
dy2
=
cy
-
ay
if
dy1
*
dx2 > dy2
*
dx1:
return
-
1
if
dx1
*
dy2 > dy1
*
dx2:
return
1
if
dx1
*
dx2 <
0
or
dy1
*
dy2 <
0
:
return
1
if
dx1
*
dx1
+
dy1
*
dy1 < dx2
*
dx2
+
dy2
*
dy2:
return
-
1
return
0
sst
=
SortedSet()
ssb
=
SortedSet()
INF30
=
1
<<
30
INF60
=
1
<<
61
sst.add(
-
INF30)
sst.add(INF30)
ssb.add(
-
INF30)
ssb.add(INF30)
x2y_t
=
{
-
INF30:
-
INF60, INF30:
-
INF60}
x2y_b
=
{
-
INF30: INF60, INF30: INF60}
buf
=
[]
q
=
int
(
input
())
for
i
in
range
(q):
x, y, a, b
=
map
(
int
,
input
().split())
if
x
in
x2y_t
and
x2y_t[x] > y:
pass
else
:
lx
=
sst.lt(x)
rx
=
sst.gt(x)
ly
=
x2y_t[lx]
ry
=
x2y_t[rx]
if
ccw(lx, ly, rx, ry, x, y) >
0
:
sst.add(x)
x2y_t[x]
=
y
left_count
=
sst.index(x)
right_count
=
sst.size
-
sst.index_right(x)
while
left_count >
=
2
:
llx
=
sst.lt(lx)
lly
=
x2y_t[llx]
if
ccw(llx, lly, x, y, lx, ly) >
0
:
break
sst.discard(lx)
del
x2y_t[lx]
left_count
-
=
1
lx, ly
=
llx, lly
while
right_count >
=
2
:
rrx
=
sst.gt(rx)
rry
=
x2y_t[rrx]
if
ccw(x, y, rrx, rry, rx, ry) >
0
:
break
sst.discard(rx)
del
x2y_t[rx]
right_count
-
=
1
rx, ry
=
rrx, rry
if
x
in
x2y_b
and
x2y_b[x] < y:
pass
else
:
lx
=
ssb.lt(x)
rx
=
ssb.gt(x)
ly
=
x2y_b[lx]
ry
=
x2y_b[rx]
if
ccw(lx, ly, rx, ry, x, y) <
0
:
ssb.add(x)
x2y_b[x]
=
y
left_count
=
ssb.index(x)
right_count
=
ssb.size
-
ssb.index_right(x)
while
left_count >
=
2
:
llx
=
ssb.lt(lx)
lly
=
x2y_b[llx]
if
ccw(llx, lly, x, y, lx, ly) <
0
:
break
ssb.discard(lx)
del
x2y_b[lx]
left_count
-
=
1
lx, ly
=
llx, lly
while
right_count >
=
2
:
rrx
=
ssb.gt(rx)
rry
=
x2y_b[rrx]
if
ccw(x, y, rrx, rry, rx, ry) <
0
:
break
ssb.discard(rx)
del
x2y_b[rx]
right_count
-
=
1
rx, ry
=
rrx, rry
if
b
=
=
0
:
if
a
=
=
0
:
buf.append(
0
)
elif
a >
0
:
x
=
sst.lt(INF30)
buf.append(a
*
x)
else
:
x
=
sst.gt(
-
INF30)
buf.append(a
*
x)
elif
b >
0
:
l
=
0
r
=
sst.size
while
l
+
1
< r:
m
=
(l
+
r)
/
/
2
x1
=
sst[m]
y1
=
x2y_t[x1]
x2
=
sst[m
+
1
]
y2
=
x2y_t[x2]
dx
=
x2
-
x1
dy
=
y2
-
y1
if
b
*
dy >
=
-
a
*
dx:
l
=
m
else
:
r
=
m
x1
=
sst[l]
x2
=
sst[r]
ans1
=
-
INF60
if
x1
=
=
-
INF30
else
a
*
x1
+
b
*
x2y_t[x1]
ans2
=
-
INF60
if
x2
=
=
INF30
else
a
*
x2
+
b
*
x2y_t[x2]
buf.append(
max
(ans1, ans2))
else
:
l
=
0
r
=
ssb.size
while
l
+
1
< r:
m
=
(l
+
r)
/
/
2
x1
=
ssb[m]
y1
=
x2y_b[x1]
x2
=
ssb[m
+
1
]
y2
=
x2y_b[x2]
dx
=
x2
-
x1
dy
=
y2
-
y1
if
b
*
dy >
=
-
a
*
dx:
l
=
m
else
:
r
=
m
x1
=
ssb[l]
x2
=
ssb[r]
ans1
=
-
INF60
if
x1
=
=
-
INF30
else
a
*
x1
+
b
*
x2y_b[x1]
ans2
=
-
INF60
if
x2
=
=
INF30
else
a
*
x2
+
b
*
x2y_b[x2]
buf.append(
max
(ans1, ans2))
print
(
'\n'
.join(
map
(
str
, buf)))