Scala Shapeless Code for Project Euler #2

beefyhalo

I've started on my Shapeless solution to Project Euler Problem #2.

I can sum together all the even fibs up to the Nth even one with this code:

import shapeless._, nat._, ops.nat.Sum

trait Fibonacci[N <: Nat] { type Out <: Nat }

object Fibonacci {
  type Aux[N <: Nat, Out0 <: Nat] = Fibonacci[N] { type Out = Out0 }

  def apply[N <: Nat](i: Nat)(implicit fib: Aux[i.N, N], n: Witness.Aux[N]):N = n.value

  implicit val fib0 = new Fibonacci[_0] { type Out = _2 }
  implicit val fib1 = new Fibonacci[_1] { type Out = _3 }

  implicit def fibN[I <: Nat, L <: Nat, M <: Nat](implicit l: Aux[I, L],
                                                  m: Aux[Succ[I], M],
                                                  sum: Sum[L, M]) =
    new Fibonacci[Succ[Succ[I]]] { type Out = sum.Out }
}

trait Fibs[N <: Nat] { type Out <: Nat }

object Fibs {
  type Aux[N <: Nat, Out0 <: Nat] = Fibs[N] { type Out = Out0 }

  def apply[N <: Nat](i: Nat)(implicit fibs: Aux[i.N, N], n: Witness.Aux[N]):N = n.value

  implicit def fibs0(implicit f: Fibonacci[_0]) = new Fibs[_0] { type Out = f.Out }

  implicit def fibsN[N <: Nat, R <: Nat, Z <: Nat](implicit fib: Fibonacci.Aux[Succ[Succ[Succ[N]]], R],
                                                   fibs: Aux[N, Z],
                                                   sum: Sum[R, Z]) =
    new Fibs[Succ[N]] {
      type Out = sum.Out
    }
}

Now I can do:

val (evenFibs0, evenFibs1) = (Fibs(0), Fibs(1))
typed[_2](evenFibs0)
typed[_10](evenFibs1)

This is how I get all even fibs: I start with the sequence 2, 3, ... , and I sum up every third Fibonacci number.

Now, I am stuck. I'd like to have functionality similar to takeWhile, so I can write a function that accepts a limit and returns the sum of my even fibs whose terms don't exceed that limit. Any ideas?

Here's my effort for what I've tried so far:

trait EvenFibsWithLimit[N <: Nat, M <: Nat] { type Out <: Nat }

trait LowPriorityFibs3 {
  type Aux[N <: Nat, M <: Nat, Out0 <: Nat] = EvenFibsWithLimit[N, M] { type Out = Out0 }

  implicit def fibs0[M <: Nat] = new EvenFibsWithLimit[_0, M] { type Out = _0 }

  implicit def fibsGT[N <: Nat, M <: Nat, O <: Nat](implicit f: EvenFibsWithLimit[N, M],
                                                    fib: Fibs.Aux[N, O],
                                                    l: ops.nat.LT[M, O]) = f
}

object EvenFibsWithLimit extends LowPriorityFibs3 {
  def apply[N <: Nat, O <: Nat](limit: Nat)(implicit fibs: Aux[N, limit.N, O],
                                            o: Witness.Aux[O]): O = o.value

  implicit def fibsN[N <: Nat, M <: Nat, O <: Nat](implicit f: EvenFibsWithLimit[N, M],
                                                   f2: Fibs.Aux[Succ[N], O],
                                                   d: ops.nat.Diff[M, O]) =
    new EvenFibsWithLimit[Succ[N], d.Out] {
      type Out = O
    }
}

The idea was to recursively subtract the limit by the output until the output is less than the limit. I can definitely smell something is off. I don't think I need Diff at all.. I've tried some other variations too, but I keep getting stuck. When I compile, I get the error diverging implicit expansion for fibsN.

EDIT:

I was thinking maybe I can construct an HList of my Fibs, and use Selector with a predicate typeclass to simulate a takeWhile. Thoughts?

Travis Brown

I find that the best way to approach this kind of problem is to step through the natural numbers while thinking about the information I need to keep track of.

On paper I'd use something like this:

N  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
C  1  2  3  3  5  5  5  8  8  8  8  8 13 13 13
P  1  1  2  2  3  3  3  5  5  5  5  5  8  8  8
L  0  2  2  2  2  2  2 10 10 10 10 10 10 10 10

Here C tracks the current number in the Fibonacci sequence—i.e. the largest one that's less than or equal to N. P is the Fibonacci number before that, and L is the current sum of the even ones we've seen so far.

We can translate this into a type class:

import shapeless._, ops.nat.{ Mod, Sum }, nat.{ _0, _1, _2 }

trait EvenFibs[N <: Nat] {
  type L <: Nat
  type C <: Nat
  type P <: Nat
}

Now there are four cases we need to handle. First I'll give the one that needs to have the lowest priority in order to get the implicit resolution right:

trait LowPriorityEvenFibs {
  type Aux[N <: Nat, L0 <: Nat, C0 <: Nat, P0 <: Nat] = EvenFibs[N] {
    type L = L0
    type C = C0
    type P = P0
  }

  implicit def ef3[N <: Nat](implicit
    ef: EvenFibs[N]
  ): Aux[Succ[N], ef.L, ef.C, ef.P] = new EvenFibs[Succ[N]] {
    type L = ef.L
    type C = ef.C
    type P = ef.P
  }
}

This is the case where Succ[N] is not a Fibonacci number. It doesn't require us to update any of the values we're keeping track of. The next case is a little more complex:

trait MidPriorityEvenFibs extends LowPriorityEvenFibs {
  implicit def ef2[N <: Nat, L0 <: Nat, PP <: Nat, P0 <: Nat](implicit
    ef: Aux[N, L0, P0, PP],
    sum: Sum.Aux[PP, P0, Succ[N]]
  ): Aux[Succ[N], L0, Succ[N], P0] = new EvenFibs[Succ[N]] {
    type L = L0
    type C = Succ[N]
    type P = P0
  }
}

This is the case where Succ[N] is an odd Fibonacci number. In this case we need to update C and P, but not L.

Our last Succ[N] case is the one where Succ[N] is an even Fibonacci number. I'll give it here with the base case:

object EvenFibs extends MidPriorityEvenFibs {
  implicit def ef0: Aux[_0, _0, _1, _1] = new EvenFibs[_0] {
    type L = _0
    type C = _1
    type P = _1
  }

  implicit def ef1[N <: Nat, L <: Nat, PP <: Nat, P0 <: Nat](implicit
    ef: Aux[N, L, P0, PP],
    sum: Sum.Aux[PP, P0, Succ[N]],
    mod: Mod.Aux[Succ[N], _2, _0],
    current: Sum[Succ[N], L]
  ): Aux[Succ[N], current.Out, Succ[N], P0] = new EvenFibs[Succ[N]] {
    type L = current.Out
    type C = Succ[N]
    type P = P0
  }

  def apply[N <: Nat](implicit ef: EvenFibs[N]): Aux[N, ef.L, ef.C, ef.P] = ef
}

Lastly we can define a helper class that will make it easier to check our work:

class ComputeHelper[N <: Nat] {
  def value[L <: Nat, C <: Nat, P <: Nat](implicit
    ef: EvenFibs.Aux[N, L, C, P],
    wit: Witness.Aux[L]
  ): L = wit.value
}

def compute[N <: Nat]: ComputeHelper[N] = new ComputeHelper[N]

And then:

test.typed[_0](compute[_0].value)
test.typed[_0](compute[_1].value)
test.typed[_2](compute[_2].value)
test.typed[_2](compute[nat._3].value)
test.typed[_2](compute[nat._4].value)
test.typed[_2](compute[nat._5].value)
test.typed[_2](compute[nat._6].value)
test.typed[_2](compute[nat._7].value)
test.typed[nat._10](compute[nat._8].value)
test.typed[nat._10](compute[nat._9].value)

The last line takes about twenty seconds to compile on my machine, but it works.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Scala Shapeless Code for Project Euler #1

From Dev

Scala - Project euler #8

From Dev

Project Euler 2 in Java

From Dev

Project Euler #2 in "Python"

From Dev

Project Euler, Number 2

From Dev

Euler project #2

From Dev

Java Code for Project Euler #12

From Dev

Slowdown by removing useless code (Project Euler 23)

From Dev

Project Euler - How is this haskell code so fast?

From Dev

Where is the NilClass in this code? (Project Euler #8)

From Dev

What is wrong with this code for Project Euler #3?

From Dev

Project Euler #7: is anything wrong in the code?

From Dev

Project Euler - How is this haskell code so fast?

From Dev

Where is the NilClass in this code? (Project Euler #8)

From Dev

What is wrong with this code for Project Euler #3?

From Dev

Project Euler Solution 9 Code Not Working

From Dev

Project Euler #1 python code not working

From Dev

the code for project euler #5 doesn't works

From Dev

Project Euler 2 python3

From Dev

Project Euler #2 Solution from the Haskell Wiki

From Dev

Incorrect Output for Project Euler #2 - Java

From Dev

What is "at" in shapeless (scala)?

From Dev

Filter usage in shapeless, Scala

From Dev

Scala shapeless zip issue

From Dev

What is "at" in shapeless (scala)?

From Dev

Scala shapeless zip issue

From Dev

What is wrong with my java code for Project Euler's program 4? (finding the largest palindrome of 2 3 digit numbers)

From Dev

Python code freezes up my computer - Project Euler 58

From Dev

Project euler 12 python code doesn't run, is it slow or what?