/*
 * Copyright (c) 2011-13 Miles Sabin 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package shapeless

package ops

import hlist.Tupler

trait TuplerInstances {
  type Aux[L <: HList, Out0] = Tupler[L] { type Out = Out0 }

  implicit def hlistTupler1[A]: Aux[A :: HNil, Tuple1[A]] = new Tupler[A :: HNil] {
    type Out = Tuple1[A]
    def apply(l : A :: HNil): Out = l match { case a :: HNil => Tuple1(a) }
  }

  implicit def hlistTupler2[A, B]: Aux[A :: B :: HNil, (A, B)] = new Tupler[A :: B :: HNil] {
    type Out = (A, B)
    def apply(l : A :: B :: HNil): Out = l match { case a :: b :: HNil => (a, b) }
  }

  implicit def hlistTupler3[A, B, C]: Aux[A :: B :: C :: HNil, (A, B, C)] = new Tupler[A :: B :: C :: HNil] {
    type Out = (A, B, C)
    def apply(l : A :: B :: C :: HNil): Out = l match { case a :: b :: c :: HNil => (a, b, c) }
  }

  implicit def hlistTupler4[A, B, C, D]: Aux[A :: B :: C :: D :: HNil, (A, B, C, D)] = new Tupler[A :: B :: C :: D :: HNil] {
    type Out = (A, B, C, D)
    def apply(l : A :: B :: C :: D :: HNil): Out = l match { case a :: b :: c :: d :: HNil => (a, b, c, d) }
  }

  implicit def hlistTupler5[A, B, C, D, E]: Aux[A :: B :: C :: D :: E :: HNil, (A, B, C, D, E)] = new Tupler[A :: B :: C :: D :: E :: HNil] {
    type Out = (A, B, C, D, E)
    def apply(l : A :: B :: C :: D :: E :: HNil): Out = l match { case a :: b :: c :: d :: e :: HNil => (a, b, c, d, e) }
  }

  implicit def hlistTupler6[A, B, C, D, E, F]: Aux[A :: B :: C :: D :: E :: F :: HNil, (A, B, C, D, E, F)] = new Tupler[A :: B :: C :: D :: E :: F :: HNil] {
    type Out = (A, B, C, D, E, F)
    def apply(l : A :: B :: C :: D :: E :: F :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: HNil => (a, b, c, d, e, f) }
  }

  implicit def hlistTupler7[A, B, C, D, E, F, G]: Aux[A :: B :: C :: D :: E :: F :: G :: HNil, (A, B, C, D, E, F, G)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: HNil] {
    type Out = (A, B, C, D, E, F, G)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: HNil => (a, b, c, d, e, f, g) }
  }

  implicit def hlistTupler8[A, B, C, D, E, F, G, H]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: HNil, (A, B, C, D, E, F, G, H)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: HNil] {
    type Out = (A, B, C, D, E, F, G, H)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: HNil => (a, b, c, d, e, f, g, h) }
  }

  implicit def hlistTupler9[A, B, C, D, E, F, G, H, I]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: HNil, (A, B, C, D, E, F, G, H, I)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: HNil => (a, b, c, d, e, f, g, h, i) }
  }

  implicit def hlistTupler10[A, B, C, D, E, F, G, H, I, J]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: HNil, (A, B, C, D, E, F, G, H, I, J)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: HNil => (a, b, c, d, e, f, g, h, i, j) }
  }

  implicit def hlistTupler11[A, B, C, D, E, F, G, H, I, J, K]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: HNil, (A, B, C, D, E, F, G, H, I, J, K)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: HNil => (a, b, c, d, e, f, g, h, i, j, k) }
  }

  implicit def hlistTupler12[A, B, C, D, E, F, G, H, I, J, K, L]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l) }
  }

  implicit def hlistTupler13[A, B, C, D, E, F, G, H, I, J, K, L, M]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m) }
  }

  implicit def hlistTupler14[A, B, C, D, E, F, G, H, I, J, K, L, M, N]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n) }
  }

  implicit def hlistTupler15[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o) }
  }

  implicit def hlistTupler16[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) }
  }

  implicit def hlistTupler17[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q) }
  }

  implicit def hlistTupler18[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r) }
  }

  implicit def hlistTupler19[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s) }
  }

  implicit def hlistTupler20[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) }
  }

  implicit def hlistTupler21[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: u :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u) }
  }

  implicit def hlistTupler22[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V]: Aux[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: V :: HNil, (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V)] = new Tupler[A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: V :: HNil] {
    type Out = (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V)
    def apply(l : A :: B :: C :: D :: E :: F :: G :: H :: I :: J :: K :: L :: M :: N :: O :: P :: Q :: R :: S :: T :: U :: V :: HNil): Out = l match { case a :: b :: c :: d :: e :: f :: g :: h :: i :: j :: k :: l :: m :: n :: o :: p :: q :: r :: s :: t :: u :: v :: HNil => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v) }
  }
}