commit c01a1f9ca48c02fd00a5ed8b5ba0da8185f4fbf6 Author: Samae Date: Sat Dec 2 17:38:53 2023 +0200 AST diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d43d807 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.cabal diff --git a/Main.hs b/Main.hs new file mode 100644 index 0000000..b6d1dab --- /dev/null +++ b/Main.hs @@ -0,0 +1,78 @@ +{-# LANGUAGE OverloadedStrings #-} + +module Main where + +import Control.Applicative ((<|>),empty) +import Data.Attoparsec.Text (Parser, parseOnly, decimal, choice, option, sepBy, endOfLine, endOfInput, many1) +import qualified Data.Maybe as M +import qualified Data.Text as T +import qualified Data.Text.IO as T + +insuranceNetworkParser :: Parser InsuranceNetworkAST +insuranceNetworkParser = many1 line + where + -- ::= ":" + -- ::= "" | "if" + line :: Parser INASTLine + line = do + id <- insurerId + ": " + pc <- percent + oe <- option [] (" if " *> exprs) + endOfLine + pure $ INASTLine id pc oe + -- ::= | "and" + exprs :: Parser [ INASTExpr ] + exprs = expr `sepBy` " and " + -- ::= | | | + expr :: Parser INASTExpr + expr = choice [ INASTExprLt <$> lt + , INASTExprGt <$> gt + , INASTExprEq <$> eq + , INASTExprInsurerId <$> insurerId ] + -- ::= "<" + lt :: Parser INASTLt + lt = "<" *> integer + -- ::= ">" + gt :: Parser INASTGt + gt = ">" *> integer + -- ::= "=" + eq :: Parser INASTEq + eq = "=" *> integer + -- ::= "I" + insurerId :: Parser INASTInsurerId + insurerId = "I" *> integer + -- ::= "%" + percent :: Parser INASTPercent + percent = integer <* "%" + -- ::= | + -- Overapproximation of the grammar + integer :: Parser Int + integer = decimal + +type InsuranceNetworkAST = [INASTLine] + +data INASTLine = INASTLine INASTInsurerId INASTPercent [INASTExpr] + deriving (Show) + +data INASTExpr = INASTExprLt INASTLt + | INASTExprGt INASTGt + | INASTExprEq INASTEq + | INASTExprInsurerId INASTInsurerId + deriving (Show) + +type INASTLt = Int +type INASTGt = Int +type INASTEq = Int +type INASTInsurerId = Int +type INASTPercent = Int + +main :: IO () +main = do + inputNetwork <- T.readFile "./input.network" + T.putStrLn "Using the following input network:" + T.putStrLn inputNetwork + T.putStrLn "Parsed AST:" + print (parseOnly insuranceNetworkParser inputNetwork) + + diff --git a/desc.md b/desc.md new file mode 100644 index 0000000..0ff53e2 --- /dev/null +++ b/desc.md @@ -0,0 +1,56 @@ +# Insurance networks + +The following grammar: + +``` + ::= | "\n" + ::= ":" + ::= "" | "if" + ::= | "and" + ::= | | | + ::= "<" + ::= ">" + ::= "=" + ::= "I" + ::= "%" + ::= | + ::= "0" | + ::= "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" +``` + +is used to represents insurance networks. + +Given a single input of type `Int`, the job of these networks is to provide, if possible, exactly 100% underwriting capital using the fewest insurers possible. Each insurer contributes their percentage only if their respective conditions are met. + +Below is a simple example of such a network: + +``` +I1: 50% +I2: 50% if <5 +I3: 40% if I1 +I4: 30% if I2 and <5 +I5: 10% if I3 and I4 +I6: 10% if I3 +``` + +where: +- Insurer `I1` contributes 50% unconditionally, for any input +- `I2` contributes 50% only if the input is `<5` +- `I3` contributes 40% only if `I1` contributes +- etc... + +Given the above, your task is to write a small command line program that takes 2 parameters: +- the path to a file that defines a network +- an integer input to the network + +and outputs a list of the fewest contributing insurers (by their integer ids). + +Eg. if your program was given the above example network and: +- input `3` then it would output `[1, 2]` (representing insurers `I1` and `I2`) +- input `6` then it would output `[1, 3, 6]` + +If a network is unable to collect exactly 100% for a given input then your program should output `[]`. + +Please create a new private github repository named `artificial-network`, commit all your code there and once finished please invite me, `pwm`, so we can check your solution. + +Good luck! \ No newline at end of file diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..361ebff --- /dev/null +++ b/flake.lock @@ -0,0 +1,24 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1628427351, + "narHash": "sha256-WuZUIQ07AvRw+T9wvQ3qFf8MXmKZ+ktZz9drNgWXDbs=", + "path": "/nix/store/aqinic6h77nrsrzwdsq2mxihw0kd87ml-source", + "rev": "348bc5de8bca09c624f5c4975f538684da4713d2", + "type": "path" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..f299920 --- /dev/null +++ b/flake.nix @@ -0,0 +1,36 @@ +{ + # inspired by: https://serokell.io/blog/practical-nix-flakes#packaging-existing-applications + description = "A Hello World in Haskell with a dependency and a devShell"; + inputs.nixpkgs.url = "nixpkgs"; + outputs = { self, nixpkgs }: + let + supportedSystems = [ "x86_64-linux" "x86_64-darwin" ]; + forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system); + nixpkgsFor = forAllSystems (system: import nixpkgs { + inherit system; + overlays = [ self.overlay ]; + }); + in + { + overlay = (final: prev: { + interview-in = final.haskellPackages.callCabal2nix "in23" ./. {}; + }); + packages = forAllSystems (system: { + interview-in = nixpkgsFor.${system}.interview-in; + }); + defaultPackage = forAllSystems (system: self.packages.${system}.interview-in); + checks = self.packages; + devShell = forAllSystems (system: let haskellPackages = nixpkgsFor.${system}.haskellPackages; + in haskellPackages.shellFor { + packages = p: [self.packages.${system}.interview-in]; + withHoogle = true; + buildInputs = with haskellPackages; [ + haskell-language-server + ghcid + cabal-install + ]; + # Change the prompt to show that you are in a devShell + shellHook = "export PS1='\\e[1;34mdev > \\e[0m'"; + }); + }; +} diff --git a/input.network b/input.network new file mode 100644 index 0000000..567158d --- /dev/null +++ b/input.network @@ -0,0 +1,6 @@ +I1: 50% +I2: 50% if <5 +I3: 40% if I1 +I4: 30% if I2 and <5 +I5: 10% if I3 and I4 +I6: 10% if I3 diff --git a/package.yaml b/package.yaml new file mode 100644 index 0000000..8978128 --- /dev/null +++ b/package.yaml @@ -0,0 +1,7 @@ +dependencies: + - base == 4.* + - attoparsec + - text +executables: + interview-in: + main: Main.hs