AST
This commit is contained in:
commit
c01a1f9ca4
7 changed files with 208 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.cabal
|
78
Main.hs
Normal file
78
Main.hs
Normal file
|
@ -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
|
||||
-- <line> ::= <insurer-id> ":" <percent> <optional-exprs>
|
||||
-- <optional-exprs> ::= "" | "if" <exprs>
|
||||
line :: Parser INASTLine
|
||||
line = do
|
||||
id <- insurerId
|
||||
": "
|
||||
pc <- percent
|
||||
oe <- option [] (" if " *> exprs)
|
||||
endOfLine
|
||||
pure $ INASTLine id pc oe
|
||||
-- <exprs> ::= <expr> | <expr> "and" <exprs>
|
||||
exprs :: Parser [ INASTExpr ]
|
||||
exprs = expr `sepBy` " and "
|
||||
-- <expr> ::= <lesser-than> | <greater-than> | <equals> | <insurer-id>
|
||||
expr :: Parser INASTExpr
|
||||
expr = choice [ INASTExprLt <$> lt
|
||||
, INASTExprGt <$> gt
|
||||
, INASTExprEq <$> eq
|
||||
, INASTExprInsurerId <$> insurerId ]
|
||||
-- <lesser-than> ::= "<" <integer>
|
||||
lt :: Parser INASTLt
|
||||
lt = "<" *> integer
|
||||
-- <greater-than> ::= ">" <integer>
|
||||
gt :: Parser INASTGt
|
||||
gt = ">" *> integer
|
||||
-- <equals> ::= "=" <integer>
|
||||
eq :: Parser INASTEq
|
||||
eq = "=" *> integer
|
||||
-- <insurer-id> ::= "I" <integer>
|
||||
insurerId :: Parser INASTInsurerId
|
||||
insurerId = "I" *> integer
|
||||
-- <percent> ::= <integer> "%"
|
||||
percent :: Parser INASTPercent
|
||||
percent = integer <* "%"
|
||||
-- <integer> ::= <digit> | <non-zero-digit> <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)
|
||||
|
||||
|
56
desc.md
Normal file
56
desc.md
Normal file
|
@ -0,0 +1,56 @@
|
|||
# Insurance networks
|
||||
|
||||
The following grammar:
|
||||
|
||||
```
|
||||
<lines> ::= <line> | <line> "\n" <lines>
|
||||
<line> ::= <insurer-id> ":" <percent> <optional-exprs>
|
||||
<optional-exprs> ::= "" | "if" <exprs>
|
||||
<exprs> ::= <expr> | <expr> "and" <exprs>
|
||||
<expr> ::= <lesser-than> | <greater-than> | <equals> | <insurer-id>
|
||||
<lesser-than> ::= "<" <integer>
|
||||
<greater-than> ::= ">" <integer>
|
||||
<equals> ::= "=" <integer>
|
||||
<insurer-id> ::= "I" <integer>
|
||||
<percent> ::= <integer> "%"
|
||||
<integer> ::= <digit> | <non-zero-digit> <integer>
|
||||
<digit> ::= "0" | <non-zero-digit>
|
||||
<non-zero-digit> ::= "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!
|
24
flake.lock
Normal file
24
flake.lock
Normal file
|
@ -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
|
||||
}
|
36
flake.nix
Normal file
36
flake.nix
Normal file
|
@ -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'";
|
||||
});
|
||||
};
|
||||
}
|
6
input.network
Normal file
6
input.network
Normal file
|
@ -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
|
7
package.yaml
Normal file
7
package.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
dependencies:
|
||||
- base == 4.*
|
||||
- attoparsec
|
||||
- text
|
||||
executables:
|
||||
interview-in:
|
||||
main: Main.hs
|
Loading…
Reference in a new issue