# Tests with variable IN tuple.

index-constraints vars=(int) index=(@1)
@1 IN (1, 2, 3)
----
[/1 - /3]

index-constraints vars=(int) index=(@1 desc)
@1 IN (1, 2, 3)
----
[/3 - /1]

index-constraints vars=(int) index=(@1) semtree-normalize
@1 IN (1, 5, 1, 4)
----
[/1 - /1]
[/4 - /5]

index-constraints vars=(int) index=(@1 desc) semtree-normalize
@1 IN (1, 5, 1, 4)
----
[/5 - /4]
[/1 - /1]

index-constraints vars=(int) index=(@1)
@1 IN (1, 2, 3, NULL)
----
[/1 - /3]

index-constraints vars=(int, int) index=(@1, @2)
@1 = 1 AND @2 IN (1, 2, 3)
----
[/1/1 - /1/3]

index-constraints vars=(int, int) index=(@1, @2 desc)
@1 = 1 AND @2 IN (1, 2, 3)
----
[/1/3 - /1/1]

index-constraints vars=(int, int) index=(@1, @2)
@1 IN (1, 2) AND @2 IN (1, 2, 3)
----
[/1/1 - /1/3]
[/2/1 - /2/3]

index-constraints vars=(int, int) index=(@1 desc, @2 desc)
@1 IN (1, 2) AND @2 IN (1, 2, 3)
----
[/2/3 - /2/1]
[/1/3 - /1/1]

index-constraints vars=(int, int) index=(@1, @2)
@1 >= 2 AND @1 <= 4 AND @2 IN (1, 2, 3)
----
[/2/1 - /4/3]
Remaining filter: @2 IN (1, 2, 3)

index-constraints vars=(int, int) index=(@1 desc, @2 desc)
@1 >= 2 AND @1 <= 4 AND @2 IN (1, 2, 3)
----
[/4/3 - /2/1]
Remaining filter: @2 IN (1, 2, 3)


index-constraints vars=(int, int) index=(@1, @2)
@1 IN (1, 2, 3) AND @2 = 4
----
[/1/4 - /1/4]
[/2/4 - /2/4]
[/3/4 - /3/4]

index-constraints vars=(int, int) index=(@1 desc, @2)
@1 IN (1, 2, 3) AND @2 = 4
----
[/3/4 - /3/4]
[/2/4 - /2/4]
[/1/4 - /1/4]

index-constraints vars=(int, int) index=(@1, @2 desc)
@1 IN (1, 2, 3) AND @2 = 4
----
[/1/4 - /1/4]
[/2/4 - /2/4]
[/3/4 - /3/4]

index-constraints vars=(int, int) index=(@1, @2)
@1 IN (1, 2, 3) AND @2 >= 2 AND @2 <= 4
----
[/1/2 - /1/4]
[/2/2 - /2/4]
[/3/2 - /3/4]

# Tests with tuple equality.

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) = (1, 2, 3)
----
[/1/2/3 - /1/2/3]

index-constraints vars=(int, int, int) index=(@1, @3)
(@1, @2, @3) = (1, 2, 3)
----
[/1/3 - /1/3]
Remaining filter: @2 = 2

index-constraints vars=(int, int, int) index=(@3, @2)
(@1, @2, @3) = (1, 2, 3)
----
[/3/2 - /3/2]
Remaining filter: @1 = 1

index-constraints vars=(int, int, int, int, int) index=(@1, @2, @3, @4, @5)
(@1, @2, 3, (4, @5)) = (1, 2, @3, (@4, 5))
----
[/1/2/3/4/5 - /1/2/3/4/5]

index-constraints vars=(int, int, int, int) index=(@1, @2, @3, @4)
(@1, @2, @3) = (1, 2, 3) AND @4 > 4
----
[/1/2/3/5 - /1/2/3]

index-constraints vars=(int, int, int, int) index=(@1, @2, @3, @4)
@1 > 5 AND @1 < 10 AND (@2, @3, @4) = (2, 3, 4)
----
[/6/2/3/4 - /9/2/3/4]
Remaining filter: ((@2 = 2) AND (@3 = 3)) AND (@4 = 4)

index-constraints \
  vars=(int, int, int, int) \
  index=(@1 desc, @2 desc, @3 desc, @4 desc)
@1 > 5 AND @1 < 10 AND (@2, @3, @4) = (2, 3, 4)
----
[/9/2/3/4 - /6/2/3/4]
Remaining filter: ((@2 = 2) AND (@3 = 3)) AND (@4 = 4)

# Tests with tuple inequalities.

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) >= (1, 2, 3)
----
[/1/2/3 - ]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) >= (1, 2, @1)
----
[/1/2 - ]
Remaining filter: (@1, @2, @3) >= (1, 2, @1)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) > (1, 2, 3)
----
[/1/2/4 - ]

index-constraints vars=(int, int, int) index=(@1, @2)
(@1, @2, @3) > (1, 2, 3)
----
[/1/2 - ]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2)
(@1, @2, @3) < (1, 2, 3)
----
(/NULL - /1/2]
Remaining filter: (@1, @2, @3) < (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) <= (1, 2, 3)
----
(/NULL - /1/2/3]
Remaining filter: (@1, @2, @3) <= (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) <= (1, 2, @1)
----
(/NULL - /1/2]
Remaining filter: (@1, @2, @3) <= (1, 2, @1)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) < (1, 2, 3)
----
(/NULL - /1/2/2]
Remaining filter: (@1, @2, @3) < (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) < (1, 2, @1)
----
(/NULL - /1/2]
Remaining filter: (@1, @2, @3) < (1, 2, @1)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/2]
[/1/2/4 - ]
Remaining filter: (@1, @2, @3) != (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 desc, @2 desc, @3 desc)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/4]
[/1/2/2 - ]
Remaining filter: (@1, @2, @3) != (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 desc, @2, @3)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/2]
[/1/2/4 - ]
Remaining filter: (@1, @2, @3) != (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 not null, @2, @3)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/2]
[/1/2/4 - ]
Remaining filter: (@1, @2, @3) != (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null, @3 not null)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/2]
[/1/2/4 - ]

index-constraints vars=(int, int, int) index=(@1, @2 not null, @3 not null)
(@1, @2, @3) != (1, 2, 3)
----
[ - /1/2/2]
[/1/2/4 - ]
Remaining filter: (@1, @2, @3) != (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) != (1, 2, @1)
----
[ - ]
Remaining filter: (@1, @2, @3) != (1, 2, @1)

index-constraints vars=(int, int, int) index=(@1 desc, @2 desc, @3 desc)
(@1, @2, @3) >= (1, 2, 3)
----
[ - /1/2/3]
Remaining filter: (@1, @2, @3) >= (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 desc, @2 desc, @3)
(@1, @2, @3) > (1, 2, 3)
----
[ - /1/2]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3 desc)
(@1, @2, @3) > (1, 2, 3)
----
[/1/2 - ]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3 desc)
(@2, @3) > (1, 2)
----
[ - ]
Remaining filter: (@2, @3) > (1, 2)

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) >= (1, 2) AND (@1, @2) <= (3, 4)
----
[/1/2 - /3/4]
Remaining filter: (@1, @2) <= (3, 4)

index-constraints vars=(int, int) index=(@1, @2) semtree-normalize
(@1, @2) BETWEEN (1, 2) AND (3, 4)
----
[/1/2 - /3/4]
Remaining filter: (@1, @2) <= (3, 4)

index-constraints vars=(int, int, int, int) index=(@1, @2, @3, @4) semtree-normalize
(@1, @2, @4) BETWEEN (1, 2, 3) AND (4, 5, 6)
----
[/1/2 - /4/5]
Remaining filter: ((@1, @2, @4) >= (1, 2, 3)) AND ((@1, @2, @4) <= (4, 5, 6))

index-constraints vars=(int, bool) index=(@1, @2)
(@1, @2) > (1, true)
----
(/1/true - ]

index-constraints vars=(int, bool) index=(@1, @2)
(@1, @2) < (1, false)
----
(/NULL - /1/false)
Remaining filter: (@1, @2) < (1, false)

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null, @3 not null)
(@1, @2, @3) <= (1, 2, 3)
----
[ - /1/2/3]

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null, @3 not null)
(@1, @2, @3) >= (1, 2, 3)
----
[/1/2/3 - ]

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null, @3 not null)
(@1, @2, @3) < (1, 2, 3)
----
[ - /1/2/2]

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null, @3 not null)
(@1, @2, @3) > (1, 2, 3)
----
[/1/2/4 - ]

index-constraints vars=(int, int, int) index=(@1, @2 not null, @3 not null)
(@1, @2, @3) <= (1, 2, 3)
----
(/NULL - /1/2/3]

index-constraints vars=(int, int, int) index=(@1, @2 not null, @3)
(@1, @2, @3) <= (1, 2, 3)
----
(/NULL - /1/2/3]
Remaining filter: (@1, @2, @3) <= (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3 not null)
(@1, @2, @3) <= (1, 2, 3)
----
(/NULL - /1/2/3]
Remaining filter: (@1, @2, @3) <= (1, 2, 3)

index-constraints \
  vars=(int, int, int) \
  index=(@1 desc not null, @2 desc not null, @3 desc not null)
(@1, @2, @3) > (1, 2, 3)
----
[ - /1/2/4]

index-constraints vars=(int, int, int) index=(@1 desc, @2 desc not null, @3 desc)
(@1, @2, @3) > (1, 2, 3)
----
[ - /1/2/4]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 desc, @2 desc, @3 desc not null)
(@1, @2, @3) > (1, 2, 3)
----
[ - /1/2/4]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @3, @2) != (1, NULL, 2)
----
[ - ]
Remaining filter: (@1, @3, @2) != (1, NULL, 2)

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null)
(@1, @2, @3) > (1, 2, 3)
----
[/1/2 - ]
Remaining filter: (@1, @2, @3) > (1, 2, 3)

index-constraints vars=(int, int, int) index=(@1 not null, @2 not null)
(@1, @2, @3) <= (1, 2, 3)
----
[ - /1/2]
Remaining filter: (@1, @2, @3) <= (1, 2, 3)

# Cases with NULLs in tuple inequalities. These conditions are true only when
# they don't depend on the NULL value, i.e. when the inequality holds true for
# the prefix up to the first NULL.

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) > (1, NULL)
----
[/2 - ]

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) >= (1, NULL)
----
[/2 - ]

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) < (1, NULL)
----
(/NULL - /0]

index-constraints vars=(int, int) index=(@1 not null, @2)
(@1, @2) < (1, NULL)
----
[ - /0]

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) <= (1, NULL)
----
(/NULL - /0]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) < (1, NULL, 1)
----
(/NULL - /0]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) >= (1, NULL, 1)
----
[/2 - ]

# TODO(radu): here we could be smarter - the condition below is equivalent to
# (@1, @3) != (1, 3).
index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) != (1, NULL, 3)
----
[ - ]
Remaining filter: (@1, @2, @3) != (1, NULL, 3)

# Tests with tuple IN tuple.

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) IN ((1, 2, 3), (4, 5, 6))
----
[/1/2/3 - /1/2/3]
[/4/5/6 - /4/5/6]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) IN ((4, 5, 6), (1, 2, 3))
----
[/1/2/3 - /1/2/3]
[/4/5/6 - /4/5/6]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) IN ((1, 2, 3), (1, 2, 3))
----
[/1/2/3 - /1/2/3]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) IN ((1, 2, 3), (4, 5, 6), (1, 2, 3))
----
[/1/2/3 - /1/2/3]
[/4/5/6 - /4/5/6]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1+5, @1, @1+@2, @2) IN ((1, 5, 1, 6), (2, 7, 2, 8), (3, 9, 3, 10))
----
[/5/6 - /5/6]
[/7/8 - /7/8]
[/9/10 - /9/10]
Remaining filter: (@1 + 5, @1, @1 + @2, @2) IN ((1, 5, 1, 6), (2, 7, 2, 8), (3, 9, 3, 10))

# Test that we properly handle NULLs inside IN tuples.
index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) IN ((1, 2), (3, NULL))
----
[/1/2 - /1/2]

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) IN ((3, NULL))
----

index-constraints vars=(int, int) index=(@1, @2)
(@1, @2) IN ((1, 2), (NULL, 4))
----
[/1/2 - /1/2]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
(@1, @2, @3) IN ((1, 2, 3), (4, 5, 6), (NULL, 8, 9))
----
[/1/2/3 - /1/2/3]
[/4/5/6 - /4/5/6]

# Verify that we sort and de-duplicate if we "project" the tuples;
# in this case the expression becomes:
#   (@1, @2) IN ((5, 5), (4, 4), (5, 5))
index-constraints vars=(int, int, int, int) index=(@2, @4)
(@1, @2, @3, @4) IN ((1, 5, 1, 5), (2, 4, 2, 4), (3, 5, 3, 5))
----
[/4/4 - /4/4]
[/5/5 - /5/5]
Remaining filter: (@1, @2, @3, @4) IN ((1, 5, 1, 5), (2, 4, 2, 4), (3, 5, 3, 5))

index-constraints vars=(int, int, int, int) index=(@2)
(@1, @2, @3, @4) IN ((1, 5, 1, 5), (2, 4, 2, 4), (3, 5, 3, 5))
----
[/4 - /5]
Remaining filter: (@1, @2, @3, @4) IN ((1, 5, 1, 5), (2, 4, 2, 4), (3, 5, 3, 5))

index-constraints vars=(int, int) index=(@1, @2)
(@2, @1) IN ((1, 5), (2, 1), (3, 4), (4, 1))
----
[/1/2 - /1/2]
[/1/4 - /1/4]
[/4/3 - /4/3]
[/5/1 - /5/1]

index-constraints vars=(int, int) index=(@1 desc, @2)
(@2, @1) IN ((1, 5), (2, 1), (3, 4), (4, 1))
----
[/5/1 - /5/1]
[/4/3 - /4/3]
[/1/2 - /1/2]
[/1/4 - /1/4]

index-constraints vars=(int, int) index=(@1, @2 desc)
(@2, @1) IN ((1, 5), (2, 1), (3, 4), (4, 1))
----
[/1/4 - /1/4]
[/1/2 - /1/2]
[/4/3 - /4/3]
[/5/1 - /5/1]

index-constraints vars=(int, int) index=(@1 desc, @2 desc)
(@2, @1) IN ((1, 5), (2, 1), (3, 4), (4, 1))
----
[/5/1 - /5/1]
[/4/3 - /4/3]
[/1/4 - /1/4]
[/1/2 - /1/2]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
@1 = 1 AND (@2, @3) IN ((2, 3), (4, 5), (6, 7))
----
[/1/2/3 - /1/2/3]
[/1/4/5 - /1/4/5]
[/1/6/7 - /1/6/7]

index-constraints vars=(int, int, int) index=(@1, @2, @3)
@3 = 1 AND (@1, @2) IN ((2, 3), (4, 5), (6, 7))
----
[/2/3/1 - /2/3/1]
[/4/5/1 - /4/5/1]
[/6/7/1 - /6/7/1]

# Here the best we can do is to effectively break up the IN constraint into
# constraints on @1 and on @3, which results in more spans than we need.
index-constraints vars=(int, int, int) index=(@1, @2, @3)
@2 = 1 AND (@1, @3) IN ((2, 3), (4, 5), (6, 7))
----
[/2/1/3 - /2/1/3]
[/2/1/5 - /2/1/5]
[/2/1/7 - /2/1/7]
[/4/1/3 - /4/1/3]
[/4/1/5 - /4/1/5]
[/4/1/7 - /4/1/7]
[/6/1/3 - /6/1/3]
[/6/1/5 - /6/1/5]
[/6/1/7 - /6/1/7]
Remaining filter: (@1, @3) IN ((2, 3), (4, 5), (6, 7))

index-constraints vars=(int, int, int) index=(@1, @2, @3)
@1 > 1 AND (@2, @3) IN ((2, 3), (4, 5), (6, 7))
----
[/2/2/3 - ]
Remaining filter: (@2, @3) IN ((2, 3), (4, 5), (6, 7))
