PU2: Finished system logic

Signed-off-by: Skyball2000 <thomas3654william@gmail.com>
main
Yan Wittmann 2022-12-13 18:17:48 +01:00 committed by Skyball2000
parent c69417493e
commit 6dbc61fa6b
3 changed files with 255 additions and 241 deletions

View File

@ -1,203 +1,96 @@
module Pflichtuebung_01.Date exposing (..)
module Date exposing (..)
import String exposing (fromInt)
type Weekday
= Mon
| Tue
| Wed
| Thu
| Fri
| Sat
| Sun
type alias Day =
Int
type alias Month =
Int
type alias Year =
Int
type alias Date =
( Day, Month, Year )
-- 1 Punkt:
year : Date -> Year
year ( _, _, y ) =
y
year (_, _, c) = c
month : Date -> Month
month ( _, m, _ ) =
m
month (_, b, _) = b
day : Date -> Day
day ( d, _, _ ) =
d
day (a, _, _) = a
-- 1 Punkt:
lt : Date -> Date -> Bool
lt ( d1, m1, y1 ) ( d2, m2, y2 ) =
(y1 < y2) || (y1 == y2 && m1 < m2) || (y1 == y2 && m1 == m2 && d1 < d2)
lt date1 date2 =
let (d1, m1, y1) = date1
(d2, m2, y2) = date2
in y1 < y2 || (y1 == y2 && (m1 < m2 || (m1 == m2 && d1 < d2)))
eq : Date -> Date -> Bool
eq ( d1, m1, y1 ) ( d2, m2, y2 ) =
y1 == y2 && m1 == m2 && d1 == d2
eq date1 date2 =
let (d1, m1, y1) = date1
(d2, m2, y2) = date2
in y1 == y2 && m1 == m2 && d1 == d2
gt : Date -> Date -> Bool
gt ( d1, m1, y1 ) ( d2, m2, y2 ) =
(y1 > y2) || (y1 == y2 && m1 > m2) || (y1 == y2 && m1 == m2 && d1 > d2)
gt date1 date2 =
let (d1, m1, y1) = date1
(d2, m2, y2) = date2
in y1 > y2 || (y1 == y2 && (m1 > m2 || (m1 == m2 && d1 > d2)))
-- 1 Punkt:
toString : Date -> String
toString ( d, m, y ) =
let
d0 =
if (d // 10) == 0 then
"0"
toString (a,b,c) = (if a < 10 then "0" ++ String.fromInt(a) else String.fromInt(a)) ++ "."
++ (if b < 10 then "0" ++ String.fromInt(b) else String.fromInt(b)) ++ "."
++ (if c < 10 then "000" ++ String.fromInt(c)
else if c < 100 then "00" ++ String.fromInt(c)
else if c < 1000 then "0" ++ String.fromInt(c)
else String.fromInt(c))
else
""
m0 =
if (m // 10) == 0 then
"0"
-- https://web.archive.org/web/20170507133619/https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
else
""
convertDateToDayNumber : Date -> Int
convertDateToDayNumber (a, b, c) =
let m = (modBy 12 (b + 9))
y = c - m // 10
in 365 * y + y // 4 - y // 100 + y // 400 + (m * 306 + 5) // 10 + ( a - 1 )
convertDayNumberToDate : Int -> Date
convertDayNumberToDate g =
let y = (10000 * g + 14780) // 3652425
ddd = g - (365 * y + y // 4 - y // 100 + y // 400)
in
d0 ++ fromInt d ++ "." ++ m0 ++ fromInt m ++ "." ++ fromInt y
next : Date -> Date
next ( d, m, y ) =
case ( d, m, y ) of
( 31, 12, _ ) ->
( 1, 1, y + 1 )
( 30, 11, _ ) ->
( 1, 12, y )
( 31, 10, _ ) ->
( 1, 11, y )
( 30, 9, _ ) ->
( 1, 10, y )
( 31, 8, _ ) ->
( 1, 9, y )
( 31, 7, _ ) ->
( 1, 8, y )
( 30, 6, _ ) ->
( 1, 7, y )
( 31, 5, _ ) ->
( 1, 6, y )
( 30, 4, _ ) ->
( 1, 5, y )
( 31, 3, _ ) ->
( 1, 4, y )
( 29, 2, _ ) ->
( 1, 3, y )
( 28, 2, _ ) ->
if leapyear y then
( 29, 2, y )
else
( 1, 3, y )
( 31, 1, _ ) ->
( 1, 2, y )
_ ->
( d + 1, m, y )
prev : Date -> Date
prev ( d, m, y ) =
case ( d, m, y ) of
( 1, 12, _ ) ->
( 30, 11, y )
( 1, 11, _ ) ->
( 31, 10, y )
( 1, 10, _ ) ->
( 30, 9, y )
( 1, 9, _ ) ->
( 31, 8, y )
( 1, 8, _ ) ->
( 31, 7, y )
( 1, 7, _ ) ->
( 30, 6, y )
( 1, 6, _ ) ->
( 31, 5, y )
( 1, 5, _ ) ->
( 30, 4, y )
( 1, 4, _ ) ->
( 31, 3, y )
( 1, 3, _ ) ->
if leapyear y then
( 29, 2, y )
else
( 28, 2, y )
( 1, 2, _ ) ->
( 31, 1, y )
( 1, 1, _ ) ->
( 31, 12, y - 1 )
_ ->
( d - 1, m, y )
if (ddd < 0) then
let y2 = y - 1 -- y = y2
ddd2 = g - (365 * y2 + y2 // 4 - y2 // 100 + y2 // 400) -- ddd = ddd2
mi = (100 * ddd2 + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y3 = y2 + (mi + 2) // 12 -- y = y3
dd = ddd2 - (mi * 306 + 5) // 10 + 1
in (dd, mm, y3)
else
let mi = (100 * ddd + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y2 = y + (mi + 2) // 12 -- y = y2
dd = ddd - (mi * 306 + 5) // 10 + 1
in (dd, mm, y2)
-- 1 Punkt:
leapyear : Year -> Bool
leapyear y =
(modBy 4 y == 0) && ((modBy 100 y /= 0) || (modBy 400 y == 0))
leapyear y = ((modBy 4 y == 0) && (modBy 100 y /= 0)) || (modBy 400 y == 0)
-- 2 Punkte:
next : Date -> Date
next date = convertDayNumberToDate ((convertDateToDayNumber date) + 1)
-- 2 Punkte:
prev : Date -> Date
prev date = convertDayNumberToDate ((convertDateToDayNumber date) - 1)
-- 2 Punkte:
sub : Date -> Date -> Int
sub d1 d2 =
subHelp 0 d1 d2
{- um optimierbare End-Rekursion zu erreichen: Akkumulator als Argument -}
subHelp : Int -> Date -> Date -> Int
subHelp diff d1 d2 =
if gt d1 d2 then
subHelp (diff + 1) d1 (next d2)
else if lt d1 d2 then
subHelp (diff - 1) d1 (prev d2)
else
diff
sub date1 date2 = convertDateToDayNumber date1 - convertDateToDayNumber date2

View File

@ -179,6 +179,8 @@ leapyear : Year -> Bool
leapyear y =
(modBy 4 y == 0) && ((modBy 100 y /= 0) || (modBy 400 y == 0))
{-
BROKEN IMPLEMENTATION:
sub : Date -> Date -> Int
sub d1 d2 =
@ -199,5 +201,39 @@ subHelp diff d1 d2 =
else
diff
-}
sub : Date -> Date -> Int
sub date1 date2 = convertDateToDayNumber date1 - convertDateToDayNumber date2
-- https://web.archive.org/web/20170507133619/https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
convertDateToDayNumber : Date -> Int
convertDateToDayNumber (a, b, c) =
let m = (modBy 12 (b + 9))
y = c - m // 10
in 365 * y + y // 4 - y // 100 + y // 400 + (m * 306 + 5) // 10 + ( a - 1 )
convertDayNumberToDate : Int -> Date
convertDayNumberToDate g =
let y = (10000 * g + 14780) // 3652425
ddd = g - (365 * y + y // 4 - y // 100 + y // 400)
in
if (ddd < 0) then
let y2 = y - 1 -- y = y2
ddd2 = g - (365 * y2 + y2 // 4 - y2 // 100 + y2 // 400) -- ddd = ddd2
mi = (100 * ddd2 + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y3 = y2 + (mi + 2) // 12 -- y = y3
dd = ddd2 - (mi * 306 + 5) // 10 + 1
in (dd, mm, y3)
else
let mi = (100 * ddd + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y2 = y + (mi + 2) // 12 -- y = y2
dd = ddd - (mi * 306 + 5) // 10 + 1
in (dd, mm, y2)

View File

@ -12,12 +12,50 @@ import Time exposing (Posix)
import Date exposing (..)
{-
NOTE:
The Date.sub implementation provided is broken. It will not terminate given specific parameters, such as (29, 2, 2022) (21, 12, 2022).
We have replaced it with our own implementation seen below, which is also more efficient, running in O(1) compared to O(n):
sub : Date -> Date -> Int
sub date1 date2 = convertDateToDayNumber date1 - convertDateToDayNumber date2
-- https://web.archive.org/web/20170507133619/https://alcor.concordia.ca/~gpkatch/gdate-algorithm.html
convertDateToDayNumber : Date -> Int
convertDateToDayNumber (a, b, c) =
let m = (modBy 12 (b + 9))
y = c - m // 10
in 365 * y + y // 4 - y // 100 + y // 400 + (m * 306 + 5) // 10 + ( a - 1 )
convertDayNumberToDate : Int -> Date
convertDayNumberToDate g =
let y = (10000 * g + 14780) // 3652425
ddd = g - (365 * y + y // 4 - y // 100 + y // 400)
in
if (ddd < 0) then
let y2 = y - 1 -- y = y2
ddd2 = g - (365 * y2 + y2 // 4 - y2 // 100 + y2 // 400) -- ddd = ddd2
mi = (100 * ddd2 + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y3 = y2 + (mi + 2) // 12 -- y = y3
dd = ddd2 - (mi * 306 + 5) // 10 + 1
in (dd, mm, y3)
else
let mi = (100 * ddd + 52) // 3060
mm = (modBy (12) (mi + 2)) + 1
y2 = y + (mi + 2) // 12 -- y = y2
dd = ddd - (mi * 306 + 5) // 10 + 1
in (dd, mm, y2)
-}
type alias Model =
{
arbeit : Arbeit
, beginndatum : Date
, besprechungsWochentag : Weekday
, anzahlWochenZwischenBesprechung : Int
, status : String
, zone : Time.Zone
, time : Time.Posix
}
@ -42,6 +80,7 @@ init _ = (
, beginndatum = (21, 12, 2022)
, besprechungsWochentag = Mon
, anzahlWochenZwischenBesprechung = 1
, status = ""
, zone = Time.utc
, time = Time.millisToPosix 0
} , Task.perform AdjustTimeZone Time.here
@ -67,17 +106,14 @@ getWeekday date =
addWeekdays : Int -> Weekday -> Weekday
addWeekdays count day =
if count < 0
then
case count of
0 -> day
_ -> addWeekdays (count + 1) (prevWeekDay day)
else
if count == 0
then
case count of
0 -> day
_ -> addWeekdays (count - 1) (nextWeekDay day)
if count < 0 then
case count of
0 -> day
_ -> addWeekdays (count + 1) (prevWeekDay day)
else if count > 0 then
case count of
0 -> day
_ -> addWeekdays (count - 1) (nextWeekDay day)
else day
nextWeekDay : Weekday -> Weekday
@ -94,13 +130,13 @@ nextWeekDay day =
prevWeekDay : Weekday -> Weekday
prevWeekDay day =
case day of
Mon -> Tue
Tue -> Wed
Wed -> Thu
Thu -> Fri
Fri -> Sat
Sat -> Sun
Sun -> Mon
Mon -> Sun
Tue -> Mon
Wed -> Tue
Thu -> Wed
Fri -> Thu
Sat -> Fri
Sun -> Sat
getMonthLength : Date -> Int
getMonthLength date =
@ -119,6 +155,8 @@ getMonthLength date =
12 -> 31
_ -> 30
{- will increment the month count by the given amount. Supports a max of 12 months to be added. Will limit the day to
the amount of days the new month has. -}
addMonthsLiterally : Int -> Date -> Date
addMonthsLiterally count date =
let newMonth = (Date.month date) + count
@ -126,6 +164,32 @@ addMonthsLiterally count date =
newDateMonthLength = getMonthLength newDate
in if Date.day newDate > newDateMonthLength then (Date.day date, newDateMonthLength, Date.year date) else newDate
skipWeekend : Date -> Date
skipWeekend date =
case getWeekday date of
Sat -> addDays 2 date
Sun -> addDays 1 date
_ -> date
{- will advance the date so long until all days in [Mon, Tue, Wed, Thu, Fri] are covered.
This does not include the first day -}
advanceByEntireWorkWeek : Date -> Date
advanceByEntireWorkWeek date =
date
|> skipDaysWhilstSkippingWeekend 5
|> skipWeekend
skipDaysWhilstSkippingWeekend : Int -> Date -> Date
skipDaysWhilstSkippingWeekend count date =
if count <= 0 then date else
skipDaysWhilstSkippingWeekend (count - 1) (date
|> skipWeekend
|> Date.next)
formatDateOutput : Date -> String
formatDateOutput date =
String.fromInt (Date.day date) ++ "." ++ String.fromInt (Date.month date) ++ "." ++ String.fromInt (Date.year date)
calculateAbgabeEndDate : Arbeit -> Date -> Date
calculateAbgabeEndDate arbeit date =
case arbeit of
@ -133,15 +197,41 @@ calculateAbgabeEndDate arbeit date =
Master -> Date.prev (addMonthsLiterally 6 date)
validiereModel : Model -> Result String String
validiereModel model =
validateModel : Model -> Result String String
validateModel model =
case model.besprechungsWochentag of
Sat -> Err "Datum darf kein Samstag sein"
Sun -> Err "Datum darf kein Sonntag sein"
_ -> Ok "Alles klärchen"
_ -> case model.beginndatum of
(day, month, year) -> if day > getMonthLength (day, month, year) || day < 0 then
Err "Tag ist außerhalb des Monats"
else if month < 1 || month > 12 then
Err "Monat muss zwischen 1 und 12 liegen"
else if year < 1000 || year > 9999 then
Err "Jahr muss größer als 1000 und kleiner als 9999 sein"
else Ok ""
findNextDateOfWeekday : Date -> Weekday -> Date
findNextDateOfWeekday date weekday =
if getWeekday date == weekday then date else findNextDateOfWeekday (Date.next date) weekday
berechneDaten : Model -> List String
berechneDaten model = [] {- TODO -}
berechneDaten model =
let beginndatum = model.beginndatum
enddatum = calculateAbgabeEndDate model.arbeit beginndatum
besprechungsWochentag = model.besprechungsWochentag
anzahlWochenZwischenBesprechung = model.anzahlWochenZwischenBesprechung
in List.concat [
((formatDateOutput beginndatum) ++ " (Beginn)") :: (berechneZwischentermine (findNextDateOfWeekday (Date.next beginndatum) besprechungsWochentag) enddatum anzahlWochenZwischenBesprechung besprechungsWochentag)
, [(formatDateOutput enddatum) ++ " (Ende)"]
]
berechneZwischentermine : Date -> Date -> Int -> Weekday -> List String
berechneZwischentermine datum enddatum wochenAbstand wochentag =
let nextBesprechung = addDays (wochenAbstand * 7) datum
isOver = Date.gt datum enddatum || Date.eq datum enddatum
in if isOver then [] else
((formatDateOutput datum) ++ " (Besprechung)") :: (berechneZwischentermine nextBesprechung enddatum wochenAbstand wochentag)
monthToIndex : Time.Month -> Int
monthToIndex month =
@ -161,47 +251,50 @@ monthToIndex month =
getCurrentDate : Model -> Date
getCurrentDate model =
let date = Time.now
in
(
Time.toDay model.zone model.time
, monthToIndex (Time.toMonth model.zone model.time)
, Time.toYear model.zone model.time
)
(
Time.toDay model.zone model.time
, monthToIndex (Time.toMonth model.zone model.time)
, Time.toYear model.zone model.time
)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
(case msg of
SetStartDate date ->
{model | beginndatum = (
case (String.split " " date) of
["asap"] -> getCurrentDate model
["ASAP"] -> getCurrentDate model
[d, m, y] -> case [String.toInt d, String.toInt m, String.toInt y] of
[Just a, Just b, Just c] -> (a, b, c)
_ -> (21, 12, 2022)
_ ->(21, 12, 2022)
) }
SetDegree arbeits ->
case arbeits of
"Bachelor" -> {model | arbeit = Bachelor}
"Master" -> {model | arbeit = Master}
_ -> model
SetInterval str ->
{model | anzahlWochenZwischenBesprechung = String.toInt str |> Maybe.withDefault 0}
SetBesprechundsWochentag weekday ->
{model | besprechungsWochentag = case weekday of
"Montag" -> Mon
"Dienstag" -> Tue
"Mittwoch" -> Wed
"Donnerstag" -> Thu
"Freitag" -> Fri
_ -> Mon
}
Tick newTime -> { model | time = newTime }
AdjustTimeZone newZone -> { model | zone = newZone }
, Cmd.none)
let newModel = case msg of
SetStartDate date ->
{model | beginndatum = (
case (String.split " " date) of
["asap"] -> advanceByEntireWorkWeek (getCurrentDate model)
["ASAP"] -> advanceByEntireWorkWeek (getCurrentDate model)
[d, m, y] -> case [String.toInt d, String.toInt m, String.toInt y] of
[Just a, Just b, Just c] -> (a, b, c)
_ -> (21, 12, 2022)
_ -> (21, 12, 2022)
) }
SetDegree arbeits ->
case arbeits of
"Bachelor" -> {model | arbeit = Bachelor}
"Master" -> {model | arbeit = Master}
_ -> model
SetInterval str ->
{model | anzahlWochenZwischenBesprechung = String.toInt str |> Maybe.withDefault 0}
SetBesprechundsWochentag weekday ->
{model | besprechungsWochentag = case weekday of
"Montag" -> Mon
"Dienstag" -> Tue
"Mittwoch" -> Wed
"Donnerstag" -> Thu
"Freitag" -> Fri
_ -> Mon
}
Tick newTime -> { model | time = newTime }
AdjustTimeZone newZone -> { model | zone = newZone }
in case validateModel newModel of
Ok a -> ({ newModel | status = case msg of
Tick i -> newModel.status
AdjustTimeZone i -> newModel.status
_ -> a
}, Cmd.none)
Err a -> ({ model | status = a }, Cmd.none)
view : Model -> Html Msg
@ -235,15 +328,6 @@ view model =
(model
|> berechneDaten
|> List.map (\datum -> li [] [text datum]))
, text (Debug.toString model.besprechungsWochentag), br[][]
, text (Debug.toString model.beginndatum), br[][]
, text (Debug.toString model.arbeit), br[][]
, text (Debug.toString model.anzahlWochenZwischenBesprechung), br[][]
, text (Debug.toString (getWeekday model.beginndatum)), br[][]
, text (Debug.toString (calculateAbgabeEndDate Bachelor model.beginndatum)), br[][]
, text (Debug.toString (calculateAbgabeEndDate Master model.beginndatum)), br[][]
, text (Debug.toString (getMonthLength model.beginndatum)), br[][]
, text (Debug.toString (Date.sub model.beginndatum (21, 12, 2022))), br[][]
]
subscriptions : Model -> Sub Msg
@ -255,4 +339,5 @@ main = Browser.element {
, update = update
, view = view
, subscriptions = subscriptions
}
}