import java.util.Scanner
object Main extends App { 
  val sc = new Scanner(System.in)
  val N = sc.nextInt()
  val W = sc.nextLong()
  val items = (for (i <- 1 to N) yield ((sc.nextLong, sc.nextLong), sc.nextInt)).toList

  type Item  = (Long, Long) // (value, Weight)
  type Items = List[Item]
  type ItemCount = (Item, Int)

  def valuePerWeight(a:Item, b:Item):Boolean = {
    val (va,wa) = a
    val (vb,wb) = b
    va.toDouble/wa > vb.toDouble/wb
  }

  def mkItems(t:ItemCount):List[Item] = {
    def iter(v:Long, w:Long, m:Int, s:Int, acc:List[Item]):(Int, List[Item]) = {
      if (m > 1) {
        iter(v << 1,w << 1,m >> 1, s << 1, (v, w)::acc)
      } else (s,acc)
    }
    val ((v,w),m) = t
    val (s, items) = iter(v, w, m, 1, Nil)
    val k = m - (s -1)
    if (k > 0) (v*k,w*k)::items else items
  }

// Greedy Knapsack solver. items have to be descending by value per weight.  
  def gks(items:Items, weight:Long):Double = {
    def iter(items:Items, weight:Long, ac:Double):Double = {
      if (items.isEmpty || weight <= 0) ac
      else {
        val (v, w) = items.head
        if (weight < w) ac+v*weight.toDouble/w
        else iter(items.tail, weight -w, ac+v)    
      }
    }
    iter(items, weight, 0f)
  }

  def solver(items:Items, weight:Long):Long = {
    if (items.isEmpty || weight == 0) 0
    else {
      val (v, w)= items.head
      if (weight >= w) {
        val v0 = solver(items.tail, weight-w) + v
        if (v0 < gks(items.tail, weight)) v0 max solver(items.tail, weight)
        else v0
      }
      else solver(items.tail, weight)
    }
  }
  
  val list = (for (e <- items) yield mkItems(e)).flatten.toList.sortWith(valuePerWeight)
  println(solver(list, W))

}
