Problem 38

The application below measures the time to calculate phi of 10090 using the algorithms from Problem 34 and Problem 37. Because it uses the Elm Architecture it will not run on elm.org/try. Instead, install Elm, compile and run this application on your local machine. See https://guide.elm-lang.org/install.html.

.

import Html exposing (div, button, p, program, text)
import Html.Events exposing (onClick)
import Task
import Time exposing (Time)


{- ======================================================
   Test 1
-}


test1 : Int -> Int
test1 n =
    test totient n


totient : Int -> Int
totient n =
    List.length <| List.filter (\x -> coprime n x) (1..n)


coprime : Int -> Int -> Bool
coprime a b =
    gcd a b == 1


gcd : Int -> Int -> Int
gcd a b =
    if b == 0 then
        abs a
    else
        gcd b (a % b)



{- ======================================================
   Test 2
-}


test2 : Int -> Int
test2 n =
    test phi n


phi : Int -> Int
phi n =
    if n < 1 then
        0
    else
        List.product
            <| List.map (\( p, m ) -> (p - 1) * p ^ (m - 1))
            <| primeFactorsM n


primeFactorsM : Int -> List ( Int, Int )
primeFactorsM n =
    toTuples <| primeFactors n


primeFactors : Int -> List Int
primeFactors n =
    if n < 2 then
        []
    else
        let
            prime =
                Maybe.withDefault 0
                    <| List.head
                    <| dropWhile (\x -> n % x /= 0) (2..n)
        in
            prime :: (primeFactors <| n // prime)


dropWhile : (a -> Bool) -> List a -> List a
dropWhile predicate list =
    case list of
        [] ->
            []

        x :: xs ->
            if (predicate x) then
                dropWhile predicate xs
            else
                list


takeWhile : (a -> Bool) -> List a -> List a
takeWhile predicate xs =
    case xs of
        [] ->
            []

        hd :: tl ->
            if (predicate hd) then
                hd :: takeWhile predicate tl
            else
                []


toTuples : List a -> List ( a, Int )
toTuples fs =
    case fs of
        [] ->
            []

        x :: xs ->
            ( x, List.length (takeWhile (\y -> y == x) fs) )
                :: toTuples (dropWhile (\y -> y == x) fs)



{- ======================================================
   Timing test app
-}


test : (Int -> Int) -> Int -> Int
test f n =
    List.length
        <| List.map f (1..n)


main =
    Html.program
        { init = init
        , view = view
        , update = update
        , subscriptions = (\_ -> Sub.none)
        }


view model =
    div []
        [ button [ onClick (ExecuteTest 1) ] [ text "Test 1" ]
        , div []
            [ text
                <| case Tuple.first model.testTimes1 of
                    Err msg ->
                        msg

                    Ok s ->
                        case Tuple.second model.testTimes1 of
                            Err msg ->
                                msg

                            Ok e ->
                                ("totient completed in " ++ toString (e - s) ++ " milliseconds.")
            , p [] []
            , text
                <| case Tuple.first model.testTimes2 of
                    Err msg ->
                        msg

                    Ok s ->
                        case Tuple.second model.testTimes2 of
                            Err msg ->
                                msg

                            Ok e ->
                                ("phi completed in " ++ toString (e - s) ++ " milliseconds.")
            ]
        , button [ onClick (ExecuteTest 2) ] [ text "Test 2" ]
        ]


type Msg
    = NoOp
    | ExecuteTest Int
    | GetTimeFailure String
    | StartTime Int Time
    | RunTest Int Time
    | EndTest Int Time


type alias TestTimes =
    ( TestTime, TestTime )


type alias TestTime =
    Result String Time


type alias Model =
    { testReps :
        Int
        -- don't hard code this or Elm will memoize the test functions
    , testTimes1 : TestTimes
    , testTimes2 : TestTimes
    }


init : ( Model, Cmd Msg )
init =
    ( { testReps = 1000
      , testTimes1 = ( Err "Please Run Test 1", Err "No end time yet!" )
      , testTimes2 = ( Err "Please Run Test 2", Err "No end time yet!" )
      }
    , Cmd.none
    )


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        EndTest n time ->
            ( case n of
                1 ->
                    { model | testTimes1 = ( Tuple.first model.testTimes1, Ok time ) }

                2 ->
                    { model | testTimes2 = ( Tuple.first model.testTimes2, Ok time ) }

                _ ->
                    model
            , Cmd.none
            )

        ExecuteTest n ->
            ( model
            , Task.perform (StartTime n) Time.now
            )

        GetTimeFailure msg ->
            ( { model | testTimes1 = ( Err "No start time yet!", Err "No end time yet!" ) }
            , Cmd.none
            )

        NoOp ->
            ( model, Cmd.none )

        RunTest n time ->
            ( case n of
                1 ->
                    { model
                        | testTimes1 = ( Ok time, Err "No end time yet!" )
                        , testReps = always model.testReps (test1 model.testReps)
                    }

                2 ->
                    { model
                        | testTimes2 = ( Ok time, Err "No end time yet!" )
                        , testReps = always model.testReps (test2 model.testReps)
                    }

                _ ->
                    model
            , Task.perform (EndTest n) Time.now
            )

        StartTime n time ->
            ( model
            , Task.perform (RunTest n) Time.now
            )



(..) : Int -> Int -> List Int
(..) start end =
    List.range start end

results matching ""

    No results matching ""