Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
2
Task/Functional-coverage-tree/00-META.yaml
Normal file
2
Task/Functional-coverage-tree/00-META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
from: http://rosettacode.org/wiki/Functional_coverage_tree
|
||||
134
Task/Functional-coverage-tree/00-TASK.txt
Normal file
134
Task/Functional-coverage-tree/00-TASK.txt
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
Functional coverage is a measure of how much a particular function of a system
|
||||
has been verified as correct. It is used heavily in tracking the completeness
|
||||
of the verification of complex System on Chip (SoC) integrated circuits, where
|
||||
it can also be used to track how well the functional ''requirements'' of the
|
||||
system have been verified.
|
||||
|
||||
This task uses a sub-set of the calculations sometimes used in tracking
|
||||
functional coverage but uses a more familiar(?) scenario.
|
||||
|
||||
;Task Description:
|
||||
The head of the clean-up crews for "The Men in a very dark shade of grey when
|
||||
viewed at night" has been tasked with managing the cleansing of two properties
|
||||
after an incident involving aliens.
|
||||
|
||||
She arranges the task hierarchically with a manager for the crews working on
|
||||
each house who return with a breakdown of how they will report on progress in
|
||||
each house.
|
||||
|
||||
The overall hierarchy of (sub)tasks is as follows,
|
||||
<pre>cleaning
|
||||
house1
|
||||
bedrooms
|
||||
bathrooms
|
||||
bathroom1
|
||||
bathroom2
|
||||
outside lavatory
|
||||
attic
|
||||
kitchen
|
||||
living rooms
|
||||
lounge
|
||||
dining room
|
||||
conservatory
|
||||
playroom
|
||||
basement
|
||||
garage
|
||||
garden
|
||||
house2
|
||||
upstairs
|
||||
bedrooms
|
||||
suite 1
|
||||
suite 2
|
||||
bedroom 3
|
||||
bedroom 4
|
||||
bathroom
|
||||
toilet
|
||||
attics
|
||||
groundfloor
|
||||
kitchen
|
||||
living rooms
|
||||
lounge
|
||||
dining room
|
||||
conservatory
|
||||
playroom
|
||||
wet room & toilet
|
||||
garage
|
||||
garden
|
||||
hot tub suite
|
||||
basement
|
||||
cellars
|
||||
wine cellar
|
||||
cinema</pre>
|
||||
|
||||
The head of cleanup knows that her managers will report fractional completion of leaf tasks (tasks with no child tasks of their own), and she knows that she will want to modify the weight of values of completion as she sees fit.
|
||||
|
||||
Some time into the cleaning, and some coverage reports have come in and she thinks see needs to weight the big house2 60-40 with respect to coverage from house1 She prefers a tabular view of her data where missing weights are assumed to be 1.0 and missing coverage 0.0.
|
||||
<pre>NAME_HIERARCHY |WEIGHT |COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |</pre>
|
||||
|
||||
;Calculation:
|
||||
The coverage of a node in the tree is calculated as the [[wp:Weighted arithmetic mean|weighted average]] of the coverage of its children evaluated bottom-upwards in the tree.
|
||||
|
||||
'''The task is to''' calculate the overall coverage of the cleaning task and display the coverage at all levels of the hierarchy on this page, in a manner that visually shows the hierarchy, weights and coverage of all nodes.
|
||||
|
||||
;Extra Credit:
|
||||
After calculating the coverage for all nodes, one can also calculate the additional/delta top level coverage that would occur if any (sub)task were to be fully covered from its current fractional coverage. This is done by multiplying the extra coverage that could be gained <math>1-coverage</math> for any node, by the product of the `powers` of its parent nodes from the top down to the node.<br>
|
||||
The power of a direct child of any parent is given by the power of the parent multiplied by the weight of the child divided by the sum of the weights of all the direct children.
|
||||
|
||||
The pseudo code would be:
|
||||
|
||||
method delta_calculation(this, power):
|
||||
sum_of_weights = sum(node.weight for node in children)
|
||||
this.delta = (1 - this.coverage) * power
|
||||
for node in self.children:
|
||||
node.delta_calculation(power * node.weight / sum_of_weights)
|
||||
return this.delta
|
||||
|
||||
Followed by a call to:
|
||||
|
||||
top.delta_calculation(power=1)
|
||||
|
||||
<br>
|
||||
'''Note:''' to aid in getting the data into your program you might want to use an alternative, more functional description of the starting data given on the discussion page.
|
||||
|
||||
154
Task/Functional-coverage-tree/Go/functional-coverage-tree.go
Normal file
154
Task/Functional-coverage-tree/Go/functional-coverage-tree.go
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type FCNode struct {
|
||||
name string
|
||||
weight int
|
||||
coverage float64
|
||||
children []*FCNode
|
||||
parent *FCNode
|
||||
}
|
||||
|
||||
func newFCN(name string, weight int, coverage float64) *FCNode {
|
||||
return &FCNode{name, weight, coverage, nil, nil}
|
||||
}
|
||||
|
||||
func (n *FCNode) addChildren(nodes []*FCNode) {
|
||||
for _, node := range nodes {
|
||||
node.parent = n
|
||||
n.children = append(n.children, node)
|
||||
}
|
||||
n.updateCoverage()
|
||||
}
|
||||
|
||||
func (n *FCNode) setCoverage(value float64) {
|
||||
if n.coverage != value {
|
||||
n.coverage = value
|
||||
// update any parent's coverage
|
||||
if n.parent != nil {
|
||||
n.parent.updateCoverage()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *FCNode) updateCoverage() {
|
||||
v1 := 0.0
|
||||
v2 := 0
|
||||
for _, node := range n.children {
|
||||
v1 += float64(node.weight) * node.coverage
|
||||
v2 += node.weight
|
||||
}
|
||||
n.setCoverage(v1 / float64(v2))
|
||||
}
|
||||
|
||||
func (n *FCNode) show(level int) {
|
||||
indent := level * 4
|
||||
nl := len(n.name) + indent
|
||||
fmt.Printf("%*s%*s %3d | %8.6f |\n", nl, n.name, 32-nl, "|", n.weight, n.coverage)
|
||||
if len(n.children) == 0 {
|
||||
return
|
||||
}
|
||||
for _, child := range n.children {
|
||||
child.show(level + 1)
|
||||
}
|
||||
}
|
||||
|
||||
var houses = []*FCNode{
|
||||
newFCN("house1", 40, 0),
|
||||
newFCN("house2", 60, 0),
|
||||
}
|
||||
|
||||
var house1 = []*FCNode{
|
||||
newFCN("bedrooms", 1, 0.25),
|
||||
newFCN("bathrooms", 1, 0),
|
||||
newFCN("attic", 1, 0.75),
|
||||
newFCN("kitchen", 1, 0.1),
|
||||
newFCN("living_rooms", 1, 0),
|
||||
newFCN("basement", 1, 0),
|
||||
newFCN("garage", 1, 0),
|
||||
newFCN("garden", 1, 0.8),
|
||||
}
|
||||
|
||||
var house2 = []*FCNode{
|
||||
newFCN("upstairs", 1, 0),
|
||||
newFCN("groundfloor", 1, 0),
|
||||
newFCN("basement", 1, 0),
|
||||
}
|
||||
|
||||
var h1Bathrooms = []*FCNode{
|
||||
newFCN("bathroom1", 1, 0.5),
|
||||
newFCN("bathroom2", 1, 0),
|
||||
newFCN("outside_lavatory", 1, 1),
|
||||
}
|
||||
|
||||
var h1LivingRooms = []*FCNode{
|
||||
newFCN("lounge", 1, 0),
|
||||
newFCN("dining_room", 1, 0),
|
||||
newFCN("conservatory", 1, 0),
|
||||
newFCN("playroom", 1, 1),
|
||||
}
|
||||
|
||||
var h2Upstairs = []*FCNode{
|
||||
newFCN("bedrooms", 1, 0),
|
||||
newFCN("bathroom", 1, 0),
|
||||
newFCN("toilet", 1, 0),
|
||||
newFCN("attics", 1, 0.6),
|
||||
}
|
||||
|
||||
var h2Groundfloor = []*FCNode{
|
||||
newFCN("kitchen", 1, 0),
|
||||
newFCN("living_rooms", 1, 0),
|
||||
newFCN("wet_room_&_toilet", 1, 0),
|
||||
newFCN("garage", 1, 0),
|
||||
newFCN("garden", 1, 0.9),
|
||||
newFCN("hot_tub_suite", 1, 1),
|
||||
}
|
||||
|
||||
var h2Basement = []*FCNode{
|
||||
newFCN("cellars", 1, 1),
|
||||
newFCN("wine_cellar", 1, 1),
|
||||
newFCN("cinema", 1, 0.75),
|
||||
}
|
||||
|
||||
var h2UpstairsBedrooms = []*FCNode{
|
||||
newFCN("suite_1", 1, 0),
|
||||
newFCN("suite_2", 1, 0),
|
||||
newFCN("bedroom_3", 1, 0),
|
||||
newFCN("bedroom_4", 1, 0),
|
||||
}
|
||||
|
||||
var h2GroundfloorLivingRooms = []*FCNode{
|
||||
newFCN("lounge", 1, 0),
|
||||
newFCN("dining_room", 1, 0),
|
||||
newFCN("conservatory", 1, 0),
|
||||
newFCN("playroom", 1, 0),
|
||||
}
|
||||
|
||||
func main() {
|
||||
cleaning := newFCN("cleaning", 1, 0)
|
||||
|
||||
house1[1].addChildren(h1Bathrooms)
|
||||
house1[4].addChildren(h1LivingRooms)
|
||||
houses[0].addChildren(house1)
|
||||
|
||||
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
|
||||
house2[0].addChildren(h2Upstairs)
|
||||
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
|
||||
house2[1].addChildren(h2Groundfloor)
|
||||
house2[2].addChildren(h2Basement)
|
||||
houses[1].addChildren(house2)
|
||||
|
||||
cleaning.addChildren(houses)
|
||||
topCoverage := cleaning.coverage
|
||||
fmt.Printf("TOP COVERAGE = %8.6f\n\n", topCoverage)
|
||||
fmt.Println("NAME HIERARCHY | WEIGHT | COVERAGE |")
|
||||
cleaning.show(0)
|
||||
|
||||
h2Basement[2].setCoverage(1) // change Cinema node coverage to 1
|
||||
diff := cleaning.coverage - topCoverage
|
||||
fmt.Println("\nIf the coverage of the Cinema node were increased from 0.75 to 1")
|
||||
fmt.Print("the top level coverage would increase by ")
|
||||
fmt.Printf("%8.6f to %8.6f\n", diff, topCoverage+diff)
|
||||
h2Basement[2].setCoverage(0.75) // restore to original value if required
|
||||
}
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
import Data.Bifunctor (first)
|
||||
import Data.Bool (bool)
|
||||
import Data.Char (isSpace)
|
||||
import qualified Data.Text as T
|
||||
import qualified Data.Text.IO as T
|
||||
import qualified Data.Text.Read as T
|
||||
import Data.Tree (Forest, Tree (..), foldTree)
|
||||
import Numeric (showFFloat)
|
||||
import System.Directory (doesFileExist)
|
||||
|
||||
----------------- FUNCTIONAL COVERAGE TREE ---------------
|
||||
|
||||
data Coverage = Coverage
|
||||
{ name :: T.Text,
|
||||
weight :: Float,
|
||||
coverage :: Float,
|
||||
share :: Float
|
||||
}
|
||||
deriving (Show)
|
||||
|
||||
--------------------------- TEST -------------------------
|
||||
fp = "./coverageOutline.txt"
|
||||
|
||||
main :: IO ()
|
||||
main =
|
||||
doesFileExist fp
|
||||
>>= bool
|
||||
(print $ "File not found: " <> fp)
|
||||
(T.readFile fp >>= T.putStrLn . updatedCoverageOutline)
|
||||
|
||||
----------------- UPDATED COVERAGE OUTLINE ---------------
|
||||
updatedCoverageOutline :: T.Text -> T.Text
|
||||
updatedCoverageOutline s =
|
||||
let delimiter = "|"
|
||||
indentedLines = T.lines s
|
||||
columnNames =
|
||||
init $
|
||||
tokenizeWith
|
||||
delimiter
|
||||
( head indentedLines
|
||||
)
|
||||
in T.unlines
|
||||
[ tabulation
|
||||
delimiter
|
||||
(columnNames <> ["SHARE OF RESIDUE"]),
|
||||
indentedLinesFromTree
|
||||
" "
|
||||
(showCoverage delimiter)
|
||||
$ withResidueShares 1.0 $
|
||||
foldTree
|
||||
weightedCoverage
|
||||
(parseTreeFromOutline delimiter indentedLines)
|
||||
]
|
||||
|
||||
------ WEIGHTED COVERAGE AND SHARES OF REMAINING WORK ----
|
||||
weightedCoverage ::
|
||||
Coverage ->
|
||||
Forest Coverage ->
|
||||
Tree Coverage
|
||||
weightedCoverage x xs =
|
||||
let cws = ((,) . coverage <*> weight) . rootLabel <$> xs
|
||||
totalWeight = foldr ((+) . snd) 0 cws
|
||||
in Node
|
||||
( x
|
||||
{ coverage =
|
||||
foldr
|
||||
(\(c, w) a -> (c * w) + a)
|
||||
(coverage x)
|
||||
cws
|
||||
/ bool 1 totalWeight (0 < totalWeight)
|
||||
}
|
||||
)
|
||||
xs
|
||||
|
||||
withResidueShares :: Float -> Tree Coverage -> Tree Coverage
|
||||
withResidueShares shareOfTotal tree =
|
||||
let go fraction node =
|
||||
let forest = subForest node
|
||||
weights = weight . rootLabel <$> forest
|
||||
weightTotal = sum weights
|
||||
nodeRoot = rootLabel node
|
||||
in Node
|
||||
( nodeRoot
|
||||
{ share =
|
||||
fraction
|
||||
* (1 - coverage nodeRoot)
|
||||
}
|
||||
)
|
||||
( zipWith
|
||||
go
|
||||
((fraction *) . (/ weightTotal) <$> weights)
|
||||
forest
|
||||
)
|
||||
in go shareOfTotal tree
|
||||
|
||||
---------------------- OUTLINE PARSE ---------------------
|
||||
parseTreeFromOutline :: T.Text -> [T.Text] -> Tree Coverage
|
||||
parseTreeFromOutline delimiter indentedLines =
|
||||
partialRecord . tokenizeWith delimiter
|
||||
<$> head
|
||||
( forestFromLineIndents $
|
||||
indentLevelsFromLines $ tail indentedLines
|
||||
)
|
||||
|
||||
forestFromLineIndents :: [(Int, T.Text)] -> [Tree T.Text]
|
||||
forestFromLineIndents pairs =
|
||||
let go [] = []
|
||||
go ((n, s) : xs) =
|
||||
let (firstTreeLines, rest) = span ((n <) . fst) xs
|
||||
in Node s (go firstTreeLines) : go rest
|
||||
in go pairs
|
||||
|
||||
indentLevelsFromLines :: [T.Text] -> [(Int, T.Text)]
|
||||
indentLevelsFromLines xs =
|
||||
let pairs = T.span isSpace <$> xs
|
||||
indentUnit =
|
||||
foldr
|
||||
( \x a ->
|
||||
let w = (T.length . fst) x
|
||||
in bool a w (w < a && 0 < w)
|
||||
)
|
||||
(maxBound :: Int)
|
||||
pairs
|
||||
in first (flip div indentUnit . T.length) <$> pairs
|
||||
|
||||
partialRecord :: [T.Text] -> Coverage
|
||||
partialRecord xs =
|
||||
let [name, weightText, coverageText] =
|
||||
take
|
||||
3
|
||||
(xs <> repeat "")
|
||||
in Coverage
|
||||
{ name = name,
|
||||
weight = defaultOrRead 1.0 weightText,
|
||||
coverage = defaultOrRead 0.0 coverageText,
|
||||
share = 0.0
|
||||
}
|
||||
|
||||
defaultOrRead :: Float -> T.Text -> Float
|
||||
defaultOrRead n txt = either (const n) fst $ T.rational txt
|
||||
|
||||
tokenizeWith :: T.Text -> T.Text -> [T.Text]
|
||||
tokenizeWith delimiter = fmap T.strip . T.splitOn delimiter
|
||||
|
||||
-------- SERIALISATION OF TREE TO TABULATED OUTLINE ------
|
||||
indentedLinesFromTree ::
|
||||
T.Text ->
|
||||
(T.Text -> a -> T.Text) ->
|
||||
Tree a ->
|
||||
T.Text
|
||||
indentedLinesFromTree tab showRoot tree =
|
||||
let go indent node =
|
||||
showRoot indent (rootLabel node) :
|
||||
(subForest node >>= go (T.append tab indent))
|
||||
in T.unlines $ go "" tree
|
||||
|
||||
showCoverage :: T.Text -> T.Text -> Coverage -> T.Text
|
||||
showCoverage delimiter indent x =
|
||||
tabulation
|
||||
delimiter
|
||||
( [T.append indent (name x), T.pack (showN 0 (weight x))]
|
||||
<> (T.pack . showN 4 <$> ([coverage, share] <*> [x]))
|
||||
)
|
||||
|
||||
tabulation :: T.Text -> [T.Text] -> T.Text
|
||||
tabulation delimiter =
|
||||
T.intercalate (T.append delimiter " ")
|
||||
. zipWith (`T.justifyLeft` ' ') [31, 9, 9, 9]
|
||||
|
||||
justifyRight :: Int -> a -> [a] -> [a]
|
||||
justifyRight n c = (drop . length) <*> (replicate n c <>)
|
||||
|
||||
showN :: Int -> Float -> String
|
||||
showN p n = justifyRight 7 ' ' (showFFloat (Just p) n "")
|
||||
45
Task/Functional-coverage-tree/J/functional-coverage-tree-1.j
Normal file
45
Task/Functional-coverage-tree/J/functional-coverage-tree-1.j
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
raw=: 0 :0
|
||||
NAME_HIERARCHY |WEIGHT |COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |
|
||||
)
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
labels=: {.<;._2;._2 raw
|
||||
'hier wspec cspec'=:|:}.<;._2;._2 raw
|
||||
level=: (%+./) (0 i.~' '&=)"1 hier
|
||||
weight=: (+ 0=]) ,".wspec
|
||||
coverage=: ,".cspec
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
merge=: ;@(({.@[,(+}.)~)&.> [: +/\1,_1}.#@>)
|
||||
unrooted=: ([: merge <@(_1,$:@}.);.1)^:(0<#)
|
||||
parent=: unrooted level
|
||||
parent_cover=: (] (1}.~.parent)}~ 1}. * %&(parent +//. ]) [)^:_
|
||||
44
Task/Functional-coverage-tree/J/functional-coverage-tree-4.j
Normal file
44
Task/Functional-coverage-tree/J/functional-coverage-tree-4.j
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
1 1 }._1 }.":labels,each ":each hier;(,.weight);,.weight parent_cover coverage
|
||||
NAME_HIERARCHY │WEIGHT │COVERAGE │
|
||||
cleaning │ 1 │0.409167 │
|
||||
house1 │40 │ 0.33125 │
|
||||
bedrooms │ 1 │ 0.25 │
|
||||
bathrooms │ 1 │ 0.5 │
|
||||
bathroom1 │ 1 │ 0.5 │
|
||||
bathroom2 │ 1 │ 0 │
|
||||
outside_lavatory │ 1 │ 1 │
|
||||
attic │ 1 │ 0.75 │
|
||||
kitchen │ 1 │ 0.1 │
|
||||
living_rooms │ 1 │ 0.25 │
|
||||
lounge │ 1 │ 0 │
|
||||
dining_room │ 1 │ 0 │
|
||||
conservatory │ 1 │ 0 │
|
||||
playroom │ 1 │ 1 │
|
||||
basement │ 1 │ 0 │
|
||||
garage │ 1 │ 0 │
|
||||
garden │ 1 │ 0.8 │
|
||||
house2 │60 │0.461111 │
|
||||
upstairs │ 1 │ 0.15 │
|
||||
bedrooms │ 1 │ 0 │
|
||||
suite_1 │ 1 │ 0 │
|
||||
suite_2 │ 1 │ 0 │
|
||||
bedroom_3 │ 1 │ 0 │
|
||||
bedroom_4 │ 1 │ 0 │
|
||||
bathroom │ 1 │ 0 │
|
||||
toilet │ 1 │ 0 │
|
||||
attics │ 1 │ 0.6 │
|
||||
groundfloor │ 1 │0.316667 │
|
||||
kitchen │ 1 │ 0 │
|
||||
living_rooms │ 1 │ 0 │
|
||||
lounge │ 1 │ 0 │
|
||||
dining_room │ 1 │ 0 │
|
||||
conservatory │ 1 │ 0 │
|
||||
playroom │ 1 │ 0 │
|
||||
wet_room_&_toilet │ 1 │ 0 │
|
||||
garage │ 1 │ 0 │
|
||||
garden │ 1 │ 0.9 │
|
||||
hot_tub_suite │ 1 │ 1 │
|
||||
basement │ 1 │0.916667 │
|
||||
cellars │ 1 │ 1 │
|
||||
wine_cellar │ 1 │ 1 │
|
||||
cinema │ 1 │ 0.75 │
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
trace=: (~.@,each (0 >. parent)&{)^:_ i.#parent
|
||||
power=: */@:{&(parent (] % (i.~ ~.)@[ { +//.) weight)@> trace
|
||||
|
||||
power*1-weight parent_cover coverage
|
||||
0.590833 0.2675 0.0375 0.025 0.00833333 0.0166667 0 0.0125 0.045 0.0375 0.0125 0.0125 0.0125 0 0.05 0.05 0.01 0.323333 0.17 0.05 0.0125 0.0125 0.0125 0.0125 0.05 0.05 0.02 0.136667 0.0333333 0.0333333 0.00833333 0.00833333 0.00833333 0.00833333 0.0333333 0.0333333 0.00333333 0 0.0166667 0 0 0.0166667
|
||||
163
Task/Functional-coverage-tree/Java/functional-coverage-tree.java
Normal file
163
Task/Functional-coverage-tree/Java/functional-coverage-tree.java
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public final class FunctionalCoverageTree {
|
||||
|
||||
public static void main(String[] aArgs) {
|
||||
FCNode cleaning = new FCNode("Cleaning", 1, 0.0);
|
||||
|
||||
List<FCNode> houses = Arrays.asList(
|
||||
new FCNode("House_1", 40, 0.0),
|
||||
new FCNode("House_2", 60, 0.0) );
|
||||
cleaning.addChildren(houses);
|
||||
|
||||
List<FCNode> house_1 = Arrays.asList(
|
||||
new FCNode("Bedrooms", 1, 0.25),
|
||||
new FCNode("Bathrooms", 1, 0.0),
|
||||
new FCNode("Attic", 1, 0.75),
|
||||
new FCNode("Kitchen", 1, 0.1),
|
||||
new FCNode("Living_rooms", 1, 0.0),
|
||||
new FCNode("Basement", 1, 0.0),
|
||||
new FCNode("Garage", 1, 0.0),
|
||||
new FCNode("Garden",1, 0.8) );
|
||||
houses.get(0).addChildren(house_1);
|
||||
|
||||
List<FCNode> bathrooms_house_1 = Arrays.asList(
|
||||
new FCNode("Bathroom_1", 1, 0.5),
|
||||
new FCNode("Bathroom_2", 1, 0.0),
|
||||
new FCNode("Outside_lavatory", 1, 1.0) );
|
||||
house_1.get(1).addChildren(bathrooms_house_1);
|
||||
|
||||
List<FCNode> living_rooms_house_1 = Arrays.asList(
|
||||
new FCNode("lounge", 1, 0.0),
|
||||
new FCNode("Dining_room", 1, 0.0),
|
||||
new FCNode("Conservatory", 1, 0.0),
|
||||
new FCNode("Playroom", 1, 1.0) );
|
||||
house_1.get(4).addChildren(living_rooms_house_1);
|
||||
|
||||
List<FCNode> house_2 = Arrays.asList(
|
||||
new FCNode("Upstairs", 1, 0.15),
|
||||
new FCNode("Ground_floor", 1, 0.316667),
|
||||
new FCNode("Basement", 1, 0.916667));
|
||||
houses.get(1).addChildren(house_2);
|
||||
|
||||
List<FCNode> upstairs = Arrays.asList(
|
||||
new FCNode("Bedrooms", 1, 0.0),
|
||||
new FCNode("Bathroom", 1, 0.0),
|
||||
new FCNode("Toilet", 1, 0.0),
|
||||
new FCNode("Attics", 1, 0.6) );
|
||||
house_2.get(0).addChildren(upstairs);
|
||||
|
||||
List<FCNode> ground_floor = Arrays.asList(
|
||||
new FCNode("Kitchen", 1, 0.0),
|
||||
new FCNode("Living_rooms", 1, 0.0),
|
||||
new FCNode("Wet_room_&_toilet", 1, 0.0),
|
||||
new FCNode("Garage", 1, 0.0),
|
||||
new FCNode("Garden", 1, 0.9),
|
||||
new FCNode("Hot_tub_suite", 1, 1.0) );
|
||||
house_2.get(1).addChildren(ground_floor);
|
||||
|
||||
List<FCNode> basement = Arrays.asList(
|
||||
new FCNode("Cellars", 1, 1.0),
|
||||
new FCNode("Wine_cellar", 1, 1.0),
|
||||
new FCNode("Cinema", 1, 0.75) );
|
||||
house_2.get(2).addChildren(basement);
|
||||
|
||||
List<FCNode> bedrooms = Arrays.asList(
|
||||
new FCNode("Suite_1", 1, 0.0),
|
||||
new FCNode("Suite_2", 1, 0.0),
|
||||
new FCNode("Bedroom_3",1, 0.0),
|
||||
new FCNode("Bedroom_4",1, 0.0) );
|
||||
upstairs.get(0).addChildren(bedrooms);
|
||||
|
||||
List<FCNode> living_rooms_house_2 = Arrays.asList(
|
||||
new FCNode("lounge", 1, 0.0),
|
||||
new FCNode("Dining_room", 1, 0.0),
|
||||
new FCNode("Conservatory", 1, 0.0),
|
||||
new FCNode("Playroom", 1, 0.0) );
|
||||
ground_floor.get(1).addChildren(living_rooms_house_2);
|
||||
|
||||
final double overallCoverage = cleaning.getCoverage();
|
||||
System.out.println("OVERALL COVERAGE = " + String.format("%.6f", overallCoverage) + System.lineSeparator());
|
||||
System.out.println("NAME HIERARCHY | WEIGHT | COVERAGE |" );
|
||||
System.out.println("--------------------------------|--------|----------|");
|
||||
cleaning.display();
|
||||
System.out.println();
|
||||
|
||||
basement.get(2).setCoverage(1.0); // Change House_2 Cinema node coverage to 1.0
|
||||
final double updatedCoverage = cleaning.getCoverage();
|
||||
final double difference = updatedCoverage - overallCoverage;
|
||||
System.out.println("If the coverage of the House_2 Cinema node were increased from 0.75 to 1.0");
|
||||
System.out.print("the overall coverage would increase by ");
|
||||
System.out.println(String.format("%.6f%s%.6f", difference, " to ", updatedCoverage));;
|
||||
basement.get(2).setCoverage(0.75); // Restore to House_2 Cinema node coverage to its original value
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final class FCNode {
|
||||
|
||||
public FCNode(String aName, int aWeight, double aCoverage) {
|
||||
name = aName;
|
||||
weight = aWeight;
|
||||
coverage = aCoverage;
|
||||
}
|
||||
|
||||
public void addChildren(List<FCNode> aNodes) {
|
||||
for ( FCNode node : aNodes ) {
|
||||
node.parent = this;
|
||||
children.add(node);
|
||||
updateCoverage();
|
||||
}
|
||||
}
|
||||
|
||||
public double getCoverage() {
|
||||
return coverage;
|
||||
}
|
||||
|
||||
public void setCoverage(double aCoverage) {
|
||||
if ( coverage != aCoverage ) {
|
||||
coverage = aCoverage;
|
||||
if ( parent != null ) {
|
||||
parent.updateCoverage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void display() {
|
||||
display(0);
|
||||
}
|
||||
|
||||
private void updateCoverage() {
|
||||
double value1 = 0.0;
|
||||
int value2 = 0;
|
||||
for ( FCNode node : children ) {
|
||||
value1 += node.weight * node.coverage;
|
||||
value2 += node.weight;
|
||||
}
|
||||
|
||||
setCoverage(value1 / value2);
|
||||
}
|
||||
|
||||
private void display(int aLevel) {
|
||||
final String initial = " ".repeat(4 * aLevel) + name;
|
||||
final String padding = " ".repeat(NAME_FIELD_WIDTH - initial.length());
|
||||
System.out.print(initial + padding + "|");
|
||||
System.out.print(" " + String.format("%3d", weight) + " |");
|
||||
System.out.println(" " + String.format("%.6f", coverage) + " |");
|
||||
|
||||
for ( FCNode child : children ) {
|
||||
child.display(aLevel + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private String name;
|
||||
private int weight;
|
||||
private double coverage;
|
||||
private FCNode parent;
|
||||
private List<FCNode> children = new ArrayList<FCNode>();
|
||||
|
||||
private static final int NAME_FIELD_WIDTH = 32;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,439 @@
|
|||
(() => {
|
||||
'use strict';
|
||||
|
||||
// updatedCoverageOutline :: String -> String
|
||||
const updatedCoverageOutline = outlineText => {
|
||||
const
|
||||
delimiter = '|',
|
||||
indentedLines = indentLevelsFromLines(lines(outlineText)),
|
||||
columns = init(tokenizeWith(delimiter)(snd(indentedLines[0])));
|
||||
|
||||
// SERIALISATION OF UPDATED PARSE TREE (TO NEW OUTLINE TEXT)
|
||||
return tabulation(delimiter)(
|
||||
columns.concat('SHARE OF RESIDUE\n')
|
||||
) + unlines(
|
||||
indentedLinesFromTree(
|
||||
showCoverage(delimiter))(' ')(
|
||||
|
||||
// TWO TRAVERSAL COMPUTATIONS
|
||||
|
||||
withResidueShares(1.0)(
|
||||
foldTree(weightedCoverage)(
|
||||
|
||||
// PARSE TREE (FROM OUTLINE TEXT)
|
||||
fmapTree(compose(
|
||||
partialRecord, tokenizeWith(delimiter)
|
||||
))(fst(
|
||||
forestFromLineIndents(tail(indentedLines))
|
||||
))
|
||||
)
|
||||
))
|
||||
);
|
||||
};
|
||||
|
||||
// TEST -----------------------------------------------
|
||||
// main :: IO ()
|
||||
const main = () =>
|
||||
console.log(
|
||||
// strOutline is included as literal text
|
||||
// at the foot of this code listing.
|
||||
updatedCoverageOutline(strOutline)
|
||||
);
|
||||
|
||||
// COVERAGE AND SHARES OF RESIDUE ---------------------
|
||||
|
||||
// weightedCoverage :: Dict -> Forest Dict -> Tree Dict
|
||||
const weightedCoverage = x => xs => {
|
||||
const
|
||||
cws = map(compose(
|
||||
fanArrow(x => x.coverage)(x => x.weight),
|
||||
root
|
||||
))(xs),
|
||||
totalWeight = cws.reduce((a, tpl) => a + snd(tpl), 0);
|
||||
return Node(
|
||||
insertDict('coverage')(
|
||||
cws.reduce((a, tpl) => {
|
||||
const [c, w] = Array.from(tpl);
|
||||
return a + (c * w);
|
||||
}, x.coverage) / (
|
||||
0 < totalWeight ? totalWeight : 1
|
||||
)
|
||||
)(x)
|
||||
)(xs);
|
||||
};
|
||||
|
||||
|
||||
// withResidueShares :: Float -> Tree Dict -> Tree Dict
|
||||
const withResidueShares = shareOfTotal => tree => {
|
||||
const go = fraction => node => {
|
||||
const
|
||||
nodeRoot = node.root,
|
||||
forest = node.nest,
|
||||
weights = forest.map(x => x.root.weight),
|
||||
weightTotal = sum(weights);
|
||||
return Node(
|
||||
insertDict('share')(
|
||||
fraction * (1 - nodeRoot.coverage)
|
||||
)(nodeRoot)
|
||||
)(
|
||||
zipWith(go)(
|
||||
weights.map(w => fraction * (w / weightTotal))
|
||||
)(forest)
|
||||
);
|
||||
};
|
||||
return go(shareOfTotal)(tree);
|
||||
};
|
||||
|
||||
|
||||
// OUTLINE PARSED TO TREE -----------------------------
|
||||
|
||||
// forestFromLineIndents :: [(Int, String)] -> [Tree String]
|
||||
const forestFromLineIndents = tuples => {
|
||||
const go = xs =>
|
||||
0 < xs.length ? (() => {
|
||||
const [n, s] = Array.from(xs[0]);
|
||||
// Lines indented under this line,
|
||||
// tupled with all the rest.
|
||||
const [firstTreeLines, rest] = Array.from(
|
||||
span(x => n < x[0])(xs.slice(1))
|
||||
);
|
||||
// This first tree, and then the rest.
|
||||
return [Node(s)(go(firstTreeLines))]
|
||||
.concat(go(rest));
|
||||
})() : [];
|
||||
return go(tuples);
|
||||
};
|
||||
|
||||
// indentLevelsFromLines :: [String] -> [(Int, String)]
|
||||
const indentLevelsFromLines = xs => {
|
||||
const
|
||||
indentTextPairs = xs.map(compose(
|
||||
firstArrow(length), span(isSpace)
|
||||
)),
|
||||
indentUnit = minimum(indentTextPairs.flatMap(pair => {
|
||||
const w = fst(pair);
|
||||
return 0 < w ? [w] : [];
|
||||
}));
|
||||
return indentTextPairs.map(
|
||||
firstArrow(flip(div)(indentUnit))
|
||||
);
|
||||
};
|
||||
|
||||
// partialRecord :: [String] -> Dict
|
||||
const partialRecord = xs => {
|
||||
const [name, weightText, coverageText] = take(3)(
|
||||
xs.concat(['', '', ''])
|
||||
);
|
||||
return {
|
||||
name: name || '?',
|
||||
weight: parseFloat(weightText) || 1.0,
|
||||
coverage: parseFloat(coverageText) || 0.0,
|
||||
share: 0.0
|
||||
};
|
||||
};
|
||||
|
||||
// tokenizeWith :: String -> String -> [String]
|
||||
const tokenizeWith = delimiter =>
|
||||
// A sequence of trimmed tokens obtained by
|
||||
// splitting s on the supplied delimiter.
|
||||
s => s.split(delimiter).map(x => x.trim());
|
||||
|
||||
|
||||
// TREE SERIALIZED TO OUTLINE -------------------------
|
||||
|
||||
// indentedLinesFromTree :: (String -> a -> String) ->
|
||||
// String -> Tree a -> [String]
|
||||
const indentedLinesFromTree = showRoot =>
|
||||
strTab => tree => {
|
||||
const go = indent =>
|
||||
node => [showRoot(indent)(node.root)]
|
||||
.concat(node.nest.flatMap(go(strTab + indent)));
|
||||
return go('')(tree);
|
||||
};
|
||||
|
||||
// showN :: Int -> Float -> String
|
||||
const showN = p =>
|
||||
n => justifyRight(7)(' ')(n.toFixed(p));
|
||||
|
||||
// showCoverage :: String -> String -> Dict -> String
|
||||
const showCoverage = delimiter =>
|
||||
indent => x => tabulation(delimiter)(
|
||||
[indent + x.name, showN(0)(x.weight)]
|
||||
.concat([x.coverage, x.share].map(showN(4)))
|
||||
);
|
||||
|
||||
// tabulation :: String -> [String] -> String
|
||||
const tabulation = delimiter =>
|
||||
// Up to 4 tokens drawn from the argument list,
|
||||
// as a single string with fixed left-justified
|
||||
// white-space widths, between delimiters.
|
||||
compose(
|
||||
intercalate(delimiter + ' '),
|
||||
zipWith(flip(justifyLeft)(' '))([31, 9, 9, 9])
|
||||
);
|
||||
|
||||
|
||||
// GENERIC AND REUSABLE FUNCTIONS ---------------------
|
||||
|
||||
// Node :: a -> [Tree a] -> Tree a
|
||||
const Node = v => xs => ({
|
||||
type: 'Node',
|
||||
root: v, // any type of value (consistent across tree)
|
||||
nest: xs || []
|
||||
});
|
||||
|
||||
// Tuple (,) :: a -> b -> (a, b)
|
||||
const Tuple = a => b => ({
|
||||
type: 'Tuple',
|
||||
'0': a,
|
||||
'1': b,
|
||||
length: 2
|
||||
});
|
||||
|
||||
// compose (<<<) :: (b -> c) -> (a -> b) -> a -> c
|
||||
const compose = (...fs) =>
|
||||
x => fs.reduceRight((a, f) => f(a), x);
|
||||
|
||||
// concat :: [[a]] -> [a]
|
||||
// concat :: [String] -> String
|
||||
const concat = xs =>
|
||||
0 < xs.length ? (() => {
|
||||
const unit = 'string' !== typeof xs[0] ? (
|
||||
[]
|
||||
) : '';
|
||||
return unit.concat.apply(unit, xs);
|
||||
})() : [];
|
||||
|
||||
// div :: Int -> Int -> Int
|
||||
const div = x => y => Math.floor(x / y);
|
||||
|
||||
// either :: (a -> c) -> (b -> c) -> Either a b -> c
|
||||
const either = fl => fr => e =>
|
||||
'Either' === e.type ? (
|
||||
undefined !== e.Left ? (
|
||||
fl(e.Left)
|
||||
) : fr(e.Right)
|
||||
) : undefined;
|
||||
|
||||
// Compose a function from a simple value to a tuple of
|
||||
// the separate outputs of two different functions
|
||||
|
||||
// fanArrow (&&&) :: (a -> b) -> (a -> c) -> (a -> (b, c))
|
||||
const fanArrow = f => g => x => Tuple(f(x))(
|
||||
g(x)
|
||||
);
|
||||
|
||||
// Lift a simple function to one which applies to a tuple,
|
||||
// transforming only the first item of the tuple
|
||||
|
||||
// firstArrow :: (a -> b) -> ((a, c) -> (b, c))
|
||||
const firstArrow = f => xy => Tuple(f(xy[0]))(
|
||||
xy[1]
|
||||
);
|
||||
|
||||
// flip :: (a -> b -> c) -> b -> a -> c
|
||||
const flip = f =>
|
||||
1 < f.length ? (
|
||||
(a, b) => f(b, a)
|
||||
) : (x => y => f(y)(x));
|
||||
|
||||
// fmapTree :: (a -> b) -> Tree a -> Tree b
|
||||
const fmapTree = f => tree => {
|
||||
const go = node => Node(f(node.root))(
|
||||
node.nest.map(go)
|
||||
);
|
||||
return go(tree);
|
||||
};
|
||||
|
||||
// foldTree :: (a -> [b] -> b) -> Tree a -> b
|
||||
const foldTree = f => tree => {
|
||||
const go = node => f(node.root)(
|
||||
node.nest.map(go)
|
||||
);
|
||||
return go(tree);
|
||||
};
|
||||
|
||||
// foldl1 :: (a -> a -> a) -> [a] -> a
|
||||
const foldl1 = f => xs =>
|
||||
1 < xs.length ? xs.slice(1)
|
||||
.reduce(uncurry(f), xs[0]) : xs[0];
|
||||
|
||||
// fst :: (a, b) -> a
|
||||
const fst = tpl => tpl[0];
|
||||
|
||||
// init :: [a] -> [a]
|
||||
const init = xs =>
|
||||
0 < xs.length ? (
|
||||
xs.slice(0, -1)
|
||||
) : undefined;
|
||||
|
||||
// insertDict :: String -> a -> Dict -> Dict
|
||||
const insertDict = k => v => dct =>
|
||||
Object.assign({}, dct, {
|
||||
[k]: v
|
||||
});
|
||||
|
||||
// intercalate :: [a] -> [[a]] -> [a]
|
||||
// intercalate :: String -> [String] -> String
|
||||
const intercalate = sep =>
|
||||
xs => xs.join(sep);
|
||||
|
||||
// isSpace :: Char -> Bool
|
||||
const isSpace = c => /\s/.test(c);
|
||||
|
||||
// justifyLeft :: Int -> Char -> String -> String
|
||||
const justifyLeft = n => cFiller => s =>
|
||||
n > s.length ? (
|
||||
s.padEnd(n, cFiller)
|
||||
) : s;
|
||||
|
||||
// justifyRight :: Int -> Char -> String -> String
|
||||
const justifyRight = n => cFiller => s =>
|
||||
n > s.length ? (
|
||||
s.padStart(n, cFiller)
|
||||
) : s;
|
||||
|
||||
// length :: [a] -> Int
|
||||
const length = xs =>
|
||||
(Array.isArray(xs) || 'string' === typeof xs) ? (
|
||||
xs.length
|
||||
) : Infinity;
|
||||
|
||||
// lines :: String -> [String]
|
||||
const lines = s => s.split(/[\r\n]/);
|
||||
|
||||
// map :: (a -> b) -> [a] -> [b]
|
||||
const map = f => xs =>
|
||||
(Array.isArray(xs) ? (
|
||||
xs
|
||||
) : xs.split('')).map(f);
|
||||
|
||||
// minimum :: Ord a => [a] -> a
|
||||
const minimum = xs =>
|
||||
0 < xs.length ? (
|
||||
foldl1(a => x => x < a ? x : a)(xs)
|
||||
) : undefined;
|
||||
|
||||
// root :: Tree a -> a
|
||||
const root = tree => tree.root;
|
||||
|
||||
// showLog :: a -> IO ()
|
||||
const showLog = (...args) =>
|
||||
console.log(
|
||||
args
|
||||
.map(JSON.stringify)
|
||||
.join(' -> ')
|
||||
);
|
||||
|
||||
// snd :: (a, b) -> b
|
||||
const snd = tpl => tpl[1];
|
||||
|
||||
// span :: (a -> Bool) -> [a] -> ([a], [a])
|
||||
const span = p => xs => {
|
||||
const iLast = xs.length - 1;
|
||||
return splitAt(
|
||||
until(i => iLast < i || !p(xs[i]))(
|
||||
succ
|
||||
)(0)
|
||||
)(xs);
|
||||
};
|
||||
|
||||
// splitAt :: Int -> [a] -> ([a], [a])
|
||||
const splitAt = n => xs =>
|
||||
Tuple(xs.slice(0, n))(
|
||||
xs.slice(n)
|
||||
);
|
||||
|
||||
// succ :: Enum a => a -> a
|
||||
const succ = x =>
|
||||
1 + x;
|
||||
|
||||
// sum :: [Num] -> Num
|
||||
const sum = xs =>
|
||||
xs.reduce((a, x) => a + x, 0);
|
||||
|
||||
// tail :: [a] -> [a]
|
||||
const tail = xs =>
|
||||
0 < xs.length ? xs.slice(1) : [];
|
||||
|
||||
// take :: Int -> [a] -> [a]
|
||||
// take :: Int -> String -> String
|
||||
const take = n => xs =>
|
||||
'GeneratorFunction' !== xs.constructor.constructor.name ? (
|
||||
xs.slice(0, n)
|
||||
) : [].concat.apply([], Array.from({
|
||||
length: n
|
||||
}, () => {
|
||||
const x = xs.next();
|
||||
return x.done ? [] : [x.value];
|
||||
}));
|
||||
|
||||
// uncurry :: (a -> b -> c) -> ((a, b) -> c)
|
||||
const uncurry = f =>
|
||||
(x, y) => f(x)(y);
|
||||
|
||||
// unlines :: [String] -> String
|
||||
const unlines = xs => xs.join('\n');
|
||||
|
||||
// until :: (a -> Bool) -> (a -> a) -> a -> a
|
||||
const until = p => f => x => {
|
||||
let v = x;
|
||||
while (!p(v)) v = f(v);
|
||||
return v;
|
||||
};
|
||||
|
||||
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
|
||||
const zipWith = f => xs => ys =>
|
||||
xs.slice(
|
||||
0, Math.min(xs.length, ys.length)
|
||||
).map((x, i) => f(x)(ys[i]));
|
||||
|
||||
// SOURCE OUTLINE -----------------------------------------
|
||||
|
||||
const strOutline = `NAME_HIERARCHY |WEIGHT |COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |`;
|
||||
|
||||
// MAIN ---
|
||||
return main();
|
||||
})();
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
using CSV, DataFrames, Formatting
|
||||
|
||||
function updatecoverage(dfname, outputname)
|
||||
df = CSV.read(dfname)
|
||||
dchild = Dict{Int, Vector{Int}}([i => Int[] for i in 0:maximum(df[!, 1])])
|
||||
|
||||
for row in eachrow(df)
|
||||
push!(dchild[row[3]], row[1])
|
||||
end
|
||||
|
||||
function coverage(t)
|
||||
return dchild[t] == [] ? df[t, :COVERAGE] * df[t, :WEIGHT] :
|
||||
sum(coverage, dchild[t]) / sum(x -> df[x, :WEIGHT], dchild[t]) * df[t, :WEIGHT]
|
||||
end
|
||||
|
||||
df[!, :COVERAGE] .= coverage.(df.NUMBER)
|
||||
|
||||
function possibleincrease(t)
|
||||
if !isempty(dchild[t])
|
||||
return 0.0
|
||||
else
|
||||
newcoverage = deepcopy(df.COVERAGE)
|
||||
newcoverage[t] = 1.0
|
||||
oldcoverage = newcoverage[1]
|
||||
function potentialcoverage(t)
|
||||
return dchild[t] == [] ? newcoverage[t] * df[t, :WEIGHT] :
|
||||
sum(potentialcoverage, dchild[t]) / sum(x -> df[x, :WEIGHT],
|
||||
dchild[t]) * df[t, :WEIGHT]
|
||||
end
|
||||
|
||||
newcoverage .= potentialcoverage.(df[!, 1])
|
||||
return newcoverage[1] - oldcoverage
|
||||
end
|
||||
end
|
||||
|
||||
df.POTENTIAL = possibleincrease.(df[!, 1])
|
||||
|
||||
CSV.write(outputname, df)
|
||||
end
|
||||
|
||||
function displaycoveragedb(dfname)
|
||||
df = CSV.read(dfname)
|
||||
indentlevel(t) = (i = 0; while (j = df[t, 3]) != 0 i += 1; t = j end; i)
|
||||
indent1 = [indentlevel(i) for i in df.NUMBER]
|
||||
maxindent = maximum(indent1)
|
||||
indent2 = maxindent .- indent1
|
||||
showpot = size(df)[2] == 6
|
||||
println("INDEX NAME_HIERARCHY WEIGHT COVERAGE (POTENTIAL INCREASE)")
|
||||
for (i, row) in enumerate(eachrow(df))
|
||||
println(rpad(row[1], 7), " "^indent1[i], rpad(row[2], 20), " "^indent2[i],
|
||||
rpad(row[4], 8), rpad(format(row[5]), 12), showpot && row[6] != 0 ? format(row[6]) : "")
|
||||
end
|
||||
end
|
||||
|
||||
const dbname = "coverage.csv"
|
||||
const newdbname = "coverageupdated.csv"
|
||||
|
||||
println("Input data:")
|
||||
displaycoveragedb(dbname)
|
||||
updatecoverage(dbname, newdbname)
|
||||
println("\nUpdated data:")
|
||||
displaycoveragedb(newdbname)
|
||||
|
|
@ -0,0 +1,138 @@
|
|||
// version 1.2.10
|
||||
|
||||
class FCNode(val name: String, val weight: Int = 1, coverage: Double = 0.0) {
|
||||
|
||||
var coverage = coverage
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
// update any parent's coverage
|
||||
if (parent != null) parent!!.updateCoverage()
|
||||
}
|
||||
}
|
||||
|
||||
val children = mutableListOf<FCNode>()
|
||||
var parent: FCNode? = null
|
||||
|
||||
fun addChildren(nodes: List<FCNode>) {
|
||||
children.addAll(nodes)
|
||||
nodes.forEach { it.parent = this }
|
||||
updateCoverage()
|
||||
}
|
||||
|
||||
private fun updateCoverage() {
|
||||
val v1 = children.sumByDouble { it.weight * it.coverage }
|
||||
val v2 = children.sumBy { it.weight }
|
||||
coverage = v1 / v2
|
||||
}
|
||||
|
||||
fun show(level: Int = 0) {
|
||||
val indent = level * 4
|
||||
val nl = name.length + indent
|
||||
print(name.padStart(nl))
|
||||
print("|".padStart(32 - nl))
|
||||
print(" %3d |".format(weight))
|
||||
println(" %8.6f |".format(coverage))
|
||||
if (children.size == 0) return
|
||||
for (child in children) child.show(level + 1)
|
||||
}
|
||||
}
|
||||
|
||||
val houses = listOf(
|
||||
FCNode("house1", 40),
|
||||
FCNode("house2", 60)
|
||||
)
|
||||
|
||||
val house1 = listOf(
|
||||
FCNode("bedrooms", 1, 0.25),
|
||||
FCNode("bathrooms"),
|
||||
FCNode("attic", 1, 0.75),
|
||||
FCNode("kitchen", 1, 0.1),
|
||||
FCNode("living_rooms"),
|
||||
FCNode("basement"),
|
||||
FCNode("garage"),
|
||||
FCNode("garden", 1, 0.8)
|
||||
)
|
||||
|
||||
val house2 = listOf(
|
||||
FCNode("upstairs"),
|
||||
FCNode("groundfloor"),
|
||||
FCNode("basement")
|
||||
)
|
||||
|
||||
val h1Bathrooms = listOf(
|
||||
FCNode("bathroom1", 1, 0.5),
|
||||
FCNode("bathroom2"),
|
||||
FCNode("outside_lavatory", 1, 1.0)
|
||||
)
|
||||
|
||||
val h1LivingRooms = listOf(
|
||||
FCNode("lounge"),
|
||||
FCNode("dining_room"),
|
||||
FCNode("conservatory"),
|
||||
FCNode("playroom", 1, 1.0)
|
||||
)
|
||||
|
||||
val h2Upstairs = listOf(
|
||||
FCNode("bedrooms"),
|
||||
FCNode("bathroom"),
|
||||
FCNode("toilet"),
|
||||
FCNode("attics", 1, 0.6)
|
||||
)
|
||||
|
||||
val h2Groundfloor = listOf(
|
||||
FCNode("kitchen"),
|
||||
FCNode("living_rooms"),
|
||||
FCNode("wet_room_&_toilet"),
|
||||
FCNode("garage"),
|
||||
FCNode("garden", 1, 0.9),
|
||||
FCNode("hot_tub_suite", 1, 1.0)
|
||||
)
|
||||
|
||||
val h2Basement = listOf(
|
||||
FCNode("cellars", 1, 1.0),
|
||||
FCNode("wine_cellar", 1, 1.0),
|
||||
FCNode("cinema", 1, 0.75)
|
||||
)
|
||||
|
||||
val h2UpstairsBedrooms = listOf(
|
||||
FCNode("suite_1"),
|
||||
FCNode("suite_2"),
|
||||
FCNode("bedroom_3"),
|
||||
FCNode("bedroom_4")
|
||||
)
|
||||
|
||||
val h2GroundfloorLivingRooms = listOf(
|
||||
FCNode("lounge"),
|
||||
FCNode("dining_room"),
|
||||
FCNode("conservatory"),
|
||||
FCNode("playroom")
|
||||
)
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val cleaning = FCNode("cleaning")
|
||||
|
||||
house1[1].addChildren(h1Bathrooms)
|
||||
house1[4].addChildren(h1LivingRooms)
|
||||
houses[0].addChildren(house1)
|
||||
|
||||
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
|
||||
house2[0].addChildren(h2Upstairs)
|
||||
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
|
||||
house2[1].addChildren(h2Groundfloor)
|
||||
house2[2].addChildren(h2Basement)
|
||||
houses[1].addChildren(house2)
|
||||
|
||||
cleaning.addChildren(houses)
|
||||
val topCoverage = cleaning.coverage
|
||||
println("TOP COVERAGE = ${"%8.6f".format(topCoverage)}\n")
|
||||
println("NAME HIERARCHY | WEIGHT | COVERAGE |")
|
||||
cleaning.show()
|
||||
|
||||
h2Basement[2].coverage = 1.0 // change Cinema node coverage to 1.0
|
||||
val diff = cleaning.coverage - topCoverage
|
||||
println("\nIf the coverage of the Cinema node were increased from 0.75 to 1.0")
|
||||
print("the top level coverage would increase by ")
|
||||
println("${"%8.6f".format(diff)} to ${"%8.6f".format(topCoverage + diff)}")
|
||||
h2Basement[2].coverage = 0.75 // restore to original value if required
|
||||
}
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
-- DATA:
|
||||
local function node(name, weight, coverage, children)
|
||||
return { name=name, weight=weight or 1.0, coverage=coverage or 0.0, sumofweights=0, delta=0, children=children }
|
||||
end
|
||||
|
||||
local root =
|
||||
node("cleaning", nil, nil, {
|
||||
node("house1", 40, nil, {
|
||||
node("bedrooms", nil, 0.25),
|
||||
node("bathrooms", nil, nil, {
|
||||
node("bathroom1", nil, 0.5),
|
||||
node("bathroom2"),
|
||||
node("outside_lavatory", nil, 1)
|
||||
}),
|
||||
node("attic", nil, 0.75),
|
||||
node("kitchen", nil, 0.1),
|
||||
node("living_rooms", nil, nil, {
|
||||
node("lounge"),
|
||||
node("dining_room"),
|
||||
node("conservatory"),
|
||||
node("playroom",nil,1)
|
||||
}),
|
||||
node("basement"),
|
||||
node("garage"),
|
||||
node("garden", nil, 0.8)
|
||||
}),
|
||||
node("house2", 60, nil, {
|
||||
node("upstairs", nil, nil, {
|
||||
node("bedrooms", nil, nil, {
|
||||
node("suite_1"),
|
||||
node("suite_2"),
|
||||
node("bedroom_3"),
|
||||
node("bedroom_4")
|
||||
}),
|
||||
node("bathroom"),
|
||||
node("toilet"),
|
||||
node("attics", nil, 0.6)
|
||||
}),
|
||||
node("groundfloor", nil, nil, {
|
||||
node("kitchen"),
|
||||
node("living_rooms", nil, nil, {
|
||||
node("lounge"),
|
||||
node("dining_room"),
|
||||
node("conservatory"),
|
||||
node("playroom")
|
||||
}),
|
||||
node("wet_room_&_toilet"),
|
||||
node("garage"),
|
||||
node("garden", nil, 0.9),
|
||||
node("hot_tub_suite", nil, 1)
|
||||
}),
|
||||
node("basement", nil, nil, {
|
||||
node("cellars", nil, 1),
|
||||
node("wine_cellar", nil, 1),
|
||||
node("cinema", nil, 0.75)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
-- TASK:
|
||||
local function calccover(node)
|
||||
if (node.children) then
|
||||
local cnt, sum = 0, 0
|
||||
for _,child in ipairs(node.children) do
|
||||
local ccnt, csum = calccover(child)
|
||||
cnt, sum = cnt+ccnt, sum+csum
|
||||
end
|
||||
node.coverage = sum/cnt
|
||||
node.sumofweights = cnt -- just as prep for extra credit
|
||||
end
|
||||
return node.weight, node.coverage * node.weight
|
||||
end
|
||||
calccover(root)
|
||||
|
||||
-- EXTRA CREDIT:
|
||||
local function calcdelta(node, power)
|
||||
node.delta = (1.0 - node.coverage) * power
|
||||
if (node.children) then
|
||||
for _,child in ipairs(node.children) do
|
||||
calcdelta(child, power * child.weight / node.sumofweights)
|
||||
end
|
||||
end
|
||||
end
|
||||
calcdelta(root,1)
|
||||
|
||||
-- OUTPUT:
|
||||
local function printnode(node, space)
|
||||
print(string.format("%-32s| %3.f | %8.6f | %8.6f |", string.rep(" ",space)..node.name, node.weight, node.coverage, node.delta))
|
||||
if node.children then
|
||||
for _,child in ipairs(node.children) do printnode(child,space+4) end
|
||||
end
|
||||
end
|
||||
print("NAME_HIERARCHY |WEIGHT |COVERAGE |DELTA |")
|
||||
printnode(root,0)
|
||||
131
Task/Functional-coverage-tree/Nim/functional-coverage-tree.nim
Normal file
131
Task/Functional-coverage-tree/Nim/functional-coverage-tree.nim
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
import strformat, strutils
|
||||
|
||||
type
|
||||
|
||||
FCNode = ref object
|
||||
name: string
|
||||
weight: int
|
||||
coverage: float
|
||||
children: seq[FCNode]
|
||||
parent: FCNode
|
||||
|
||||
func newFCNode(name: string; weight: int; coverage: float): FCNode =
|
||||
FCNode(name: name, weight: weight, coverage: coverage)
|
||||
|
||||
# Forward reference.
|
||||
func updateCoverage(n: FCNode)
|
||||
|
||||
func addChildren(n: FCNode; nodes: openArray[FCNode]) =
|
||||
for node in nodes:
|
||||
node.parent = n
|
||||
n.children = @nodes
|
||||
n.updateCoverage()
|
||||
|
||||
func setCoverage(n: FCNode; value: float) =
|
||||
if n.coverage != value:
|
||||
n.coverage = value
|
||||
# Update any parent's coverage.
|
||||
if not n.parent.isNil:
|
||||
n.parent.updateCoverage()
|
||||
|
||||
func updateCoverage(n: FCNode) =
|
||||
var v1 = 0.0
|
||||
var v2 = 0
|
||||
for node in n.children:
|
||||
v1 += node.weight.toFloat * node.coverage
|
||||
v2 += node.weight
|
||||
n.setCoverage(v1 / v2.toFloat)
|
||||
|
||||
proc show(n: FCNode; level: int) =
|
||||
let indent = level * 4
|
||||
let nl = n.name.len + indent
|
||||
const Sep = "|"
|
||||
echo &"{n.name.align(nl)}{Sep.align(32-nl)} {n.weight:>3d} | {n.coverage:8.6f} |"
|
||||
for child in n.children:
|
||||
child.show(level + 1)
|
||||
|
||||
#———————————————————————————————————————————————————————————————————————————————————————————————————
|
||||
|
||||
let houses = [newFCNode("house1", 40, 0), newFCNode("house2", 60, 0)]
|
||||
|
||||
let house1 = [
|
||||
newFCNode("bedrooms", 1, 0.25),
|
||||
newFCNode("bathrooms", 1, 0),
|
||||
newFCNode("attic", 1, 0.75),
|
||||
newFCNode("kitchen", 1, 0.1),
|
||||
newFCNode("living_rooms", 1, 0),
|
||||
newFCNode("basement", 1, 0),
|
||||
newFCNode("garage", 1, 0),
|
||||
newFCNode("garden", 1, 0.8)]
|
||||
|
||||
let house2 = [
|
||||
newFCNode("upstairs", 1, 0),
|
||||
newFCNode("groundfloor", 1, 0),
|
||||
newFCNode("basement", 1, 0)]
|
||||
|
||||
let h1Bathrooms = [
|
||||
newFCNode("bathroom1", 1, 0.5),
|
||||
newFCNode("bathroom2", 1, 0),
|
||||
newFCNode("outside_lavatory", 1, 1)]
|
||||
|
||||
let h1LivingRooms = [
|
||||
newFCNode("lounge", 1, 0),
|
||||
newFCNode("dining_room", 1, 0),
|
||||
newFCNode("conservatory", 1, 0),
|
||||
newFCNode("playroom", 1, 1)]
|
||||
|
||||
let h2Upstairs = [
|
||||
newFCNode("bedrooms", 1, 0),
|
||||
newFCNode("bathroom", 1, 0),
|
||||
newFCNode("toilet", 1, 0),
|
||||
newFCNode("attics", 1, 0.6)]
|
||||
|
||||
let h2Groundfloor = [
|
||||
newFCNode("kitchen", 1, 0),
|
||||
newFCNode("living_rooms", 1, 0),
|
||||
newFCNode("wet_room_&_toilet", 1, 0),
|
||||
newFCNode("garage", 1, 0),
|
||||
newFCNode("garden", 1, 0.9),
|
||||
newFCNode("hot_tub_suite", 1, 1)]
|
||||
|
||||
let h2Basement = [
|
||||
newFCNode("cellars", 1, 1),
|
||||
newFCNode("wine_cellar", 1, 1),
|
||||
newFCNode("cinema", 1, 0.75)]
|
||||
|
||||
let h2UpstairsBedrooms = [
|
||||
newFCNode("suite_1", 1, 0),
|
||||
newFCNode("suite_2", 1, 0),
|
||||
newFCNode("bedroom_3", 1, 0),
|
||||
newFCNode("bedroom_4", 1, 0)]
|
||||
|
||||
let h2GroundfloorLivingRooms = [
|
||||
newFCNode("lounge", 1, 0),
|
||||
newFCNode("dining_room", 1, 0),
|
||||
newFCNode("conservatory", 1, 0),
|
||||
newFCNode("playroom", 1, 0)]
|
||||
|
||||
let cleaning = newFCNode("cleaning", 1, 0)
|
||||
|
||||
house1[1].addChildren(h1Bathrooms)
|
||||
house1[4].addChildren(h1LivingRooms)
|
||||
houses[0].addChildren(house1)
|
||||
|
||||
h2Upstairs[0].addChildren(h2UpstairsBedrooms)
|
||||
house2[0].addChildren(h2Upstairs)
|
||||
h2Groundfloor[1].addChildren(h2GroundfloorLivingRooms)
|
||||
house2[1].addChildren(h2Groundfloor)
|
||||
house2[2].addChildren(h2Basement)
|
||||
houses[1].addChildren(house2)
|
||||
|
||||
cleaning.addChildren(houses)
|
||||
let topCoverage = cleaning.coverage
|
||||
echo &"TOP COVERAGE = {topCoverage:8.6f}\n"
|
||||
echo "NAME HIERARCHY | WEIGHT | COVERAGE |"
|
||||
cleaning.show(0)
|
||||
|
||||
h2Basement[2].setCoverage(1) # Change Cinema node coverage to 1.
|
||||
let diff = cleaning.coverage - topCoverage
|
||||
echo "\nIf the coverage of the Cinema node were increased from 0.75 to 1"
|
||||
echo &"the top level coverage would increase by {diff:8.6f} to {topCoverage + diff:8.6f}"
|
||||
h2Basement[2].setCoverage(0.75) # Restore to original value if required.
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub walktree {
|
||||
my @parts;
|
||||
while( $_[0] =~ /(?<head> (\s*) \N+\n ) # split off one level as 'head' (or terminal 'leaf')
|
||||
(?<body> (?:\2 \s\N+\n)*)/gx ) { # next sub-level is 'body' (defined by extra depth of indentation)
|
||||
|
||||
my($head, $body) = ($+{head}, $+{body});
|
||||
$head =~ /^.*? \| # ignore name
|
||||
(\S*) \s* \| # save weight
|
||||
(\S*) /x; # save coverage
|
||||
my $weight = sprintf '%-8s', $1 || 1;
|
||||
my $coverage = sprintf '%-10s', $2 || 0;
|
||||
my($w, $wsum) = (0, 0);
|
||||
|
||||
$head .= $_->[0],
|
||||
$w += $_->[1],
|
||||
$wsum += $_->[1] * $_->[2]
|
||||
for walktree( $body );
|
||||
|
||||
$coverage = sprintf '%-10.2g', $wsum/$w unless $w == 0;
|
||||
push @parts, [ $head =~ s/\|.*/|$weight|$coverage|/r, $weight, $coverage ];
|
||||
}
|
||||
return @parts;
|
||||
}
|
||||
|
||||
print $_->[0] for walktree( join '', <DATA> );
|
||||
|
||||
__DATA__
|
||||
NAME_HIERARCHY |WEIGHT |COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |
|
||||
113
Task/Functional-coverage-tree/Phix/functional-coverage-tree.phix
Normal file
113
Task/Functional-coverage-tree/Phix/functional-coverage-tree.phix
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #000080;font-style:italic;">--
|
||||
-- demo\rosetta\Functional_coverage_tree.exw
|
||||
-- =========================================
|
||||
--</span>
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">data</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"""
|
||||
NAME_HIERARCHY | WEIGHT | COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |
|
||||
"""</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">lines</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">data</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">),</span>
|
||||
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indents (to locate parents)</span>
|
||||
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span> <span style="color: #000080;font-style:italic;">-- indexes for ""</span>
|
||||
<span style="color: #000000;">children</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weights</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">covers</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">child</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">childw</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #008080;">enum</span> <span style="color: #000000;">DESC</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">PARENT</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">CHILDW</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">" SHARE OF RESIDUE"</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">2</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000080;font-style:italic;">-- decode text to useable data, link up parents & children</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">weights</span><span style="color: #0000FF;">,</span><span style="color: #000000;">covers</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #008000;">"|"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000080;font-style:italic;">-- (nb this assumes /totally/ consistent indenting)</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">indent</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">)-</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim_head</span><span style="color: #0000FF;">(</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">)),</span>
|
||||
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">,</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">pi</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">,</span><span style="color: #000000;">indent</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">pdx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pdx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">pi</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
|
||||
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">k</span><span style="color: #0000FF;">></span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">parent</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">pdx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000080;font-style:italic;">-- lines[parent][CHILDREN] &= i</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">deep_copy</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">])</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">i</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
|
||||
<span style="color: #000000;">weight</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">weights</span><span style="color: #0000FF;">),</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">trim</span><span style="color: #0000FF;">(</span><span style="color: #000000;">covers</span><span style="color: #0000FF;">),</span><span style="color: #000000;">0</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">childw</span><span style="color: #0000FF;">}</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">2</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000080;font-style:italic;">-- calculate the parent coverages, and save child weight sums</span>
|
||||
<span style="color: #000000;">children</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDREN</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #000000;">childw</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">children</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">children</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">w</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000000;">coverage</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">]*</span><span style="color: #000000;">w</span>
|
||||
<span style="color: #000000;">childw</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">w</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">COVERAGE</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">/</span><span style="color: #000000;">childw</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDW</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">childw</span> <span style="color: #000080;font-style:italic;">-- (save for next loop)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">to</span> <span style="color: #000000;">2</span> <span style="color: #008080;">by</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000080;font-style:italic;">-- calculate the share of residue, and format lines</span>
|
||||
<span style="color: #000000;">child</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">i</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">residue</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span><span style="color: #0000FF;">-</span><span style="color: #000000;">coverage</span>
|
||||
<span style="color: #008080;">while</span> <span style="color: #000000;">parent</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">residue</span> <span style="color: #0000FF;">*=</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">child</span><span style="color: #0000FF;">][</span><span style="color: #000000;">WEIGHT</span><span style="color: #0000FF;">]/</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">CHILDW</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #000000;">child</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">parent</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">parent</span><span style="color: #0000FF;">][</span><span style="color: #000000;">PARENT</span><span style="color: #0000FF;">]}</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
|
||||
<span style="color: #000000;">lines</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"%-32s| %6d | %-8g | %g"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">desc</span><span style="color: #0000FF;">,</span><span style="color: #000000;">weight</span><span style="color: #0000FF;">,</span><span style="color: #000000;">coverage</span><span style="color: #0000FF;">,</span><span style="color: #000000;">residue</span><span style="color: #0000FF;">})</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lines</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)&</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
|
||||
<!--
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
from itertools import zip_longest
|
||||
|
||||
|
||||
fc2 = '''\
|
||||
cleaning,,
|
||||
house1,40,
|
||||
bedrooms,,.25
|
||||
bathrooms,,
|
||||
bathroom1,,.5
|
||||
bathroom2,,
|
||||
outside_lavatory,,1
|
||||
attic,,.75
|
||||
kitchen,,.1
|
||||
living_rooms,,
|
||||
lounge,,
|
||||
dining_room,,
|
||||
conservatory,,
|
||||
playroom,,1
|
||||
basement,,
|
||||
garage,,
|
||||
garden,,.8
|
||||
house2,60,
|
||||
upstairs,,
|
||||
bedrooms,,
|
||||
suite_1,,
|
||||
suite_2,,
|
||||
bedroom_3,,
|
||||
bedroom_4,,
|
||||
bathroom,,
|
||||
toilet,,
|
||||
attics,,.6
|
||||
groundfloor,,
|
||||
kitchen,,
|
||||
living_rooms,,
|
||||
lounge,,
|
||||
dining_room,,
|
||||
conservatory,,
|
||||
playroom,,
|
||||
wet_room_&_toilet,,
|
||||
garage,,
|
||||
garden,,.9
|
||||
hot_tub_suite,,1
|
||||
basement,,
|
||||
cellars,,1
|
||||
wine_cellar,,1
|
||||
cinema,,.75
|
||||
|
||||
'''
|
||||
|
||||
NAME, WT, COV = 0, 1, 2
|
||||
|
||||
def right_type(txt):
|
||||
try:
|
||||
return float(txt)
|
||||
except ValueError:
|
||||
return txt
|
||||
|
||||
def commas_to_list(the_list, lines, start_indent=0):
|
||||
'''
|
||||
Output format is a nest of lists and tuples
|
||||
lists are for coverage leaves without children items in the list are name, weight, coverage
|
||||
tuples are 2-tuples for nodes with children. The first element is a list representing the
|
||||
name, weight, coverage of the node (some to be calculated); the second element is a list of
|
||||
child elements which may be 2-tuples or lists as above.
|
||||
|
||||
the_list is modified in-place
|
||||
lines must be a generator of successive lines of input like fc2
|
||||
'''
|
||||
for n, line in lines:
|
||||
indent = 0
|
||||
while line.startswith(' ' * (4 * indent)):
|
||||
indent += 1
|
||||
indent -= 1
|
||||
fields = [right_type(f) for f in line.strip().split(',')]
|
||||
if indent == start_indent:
|
||||
the_list.append(fields)
|
||||
elif indent > start_indent:
|
||||
lst = [fields]
|
||||
sub = commas_to_list(lst, lines, indent)
|
||||
the_list[-1] = (the_list[-1], lst)
|
||||
if sub not in (None, ['']) :
|
||||
the_list.append(sub)
|
||||
else:
|
||||
return fields if fields else None
|
||||
return None
|
||||
|
||||
|
||||
def pptreefields(lst, indent=0, widths=['%-32s', '%-8g', '%-10g']):
|
||||
'''
|
||||
Pretty prints the format described from function commas_to_list as a table with
|
||||
names in the first column suitably indented and all columns having a fixed
|
||||
minimum column width.
|
||||
'''
|
||||
lhs = ' ' * (4 * indent)
|
||||
for item in lst:
|
||||
if type(item) != tuple:
|
||||
name, *rest = item
|
||||
print(widths[0] % (lhs + name), end='|')
|
||||
for width, item in zip_longest(widths[1:len(rest)], rest, fillvalue=widths[-1]):
|
||||
if type(item) == str:
|
||||
width = width[:-1] + 's'
|
||||
print(width % item, end='|')
|
||||
print()
|
||||
else:
|
||||
item, children = item
|
||||
name, *rest = item
|
||||
print(widths[0] % (lhs + name), end='|')
|
||||
for width, item in zip_longest(widths[1:len(rest)], rest, fillvalue=widths[-1]):
|
||||
if type(item) == str:
|
||||
width = width[:-1] + 's'
|
||||
print(width % item, end='|')
|
||||
print()
|
||||
pptreefields(children, indent+1)
|
||||
|
||||
|
||||
def default_field(node_list):
|
||||
node_list[WT] = node_list[WT] if node_list[WT] else 1.0
|
||||
node_list[COV] = node_list[COV] if node_list[COV] else 0.0
|
||||
|
||||
def depth_first(tree, visitor=default_field):
|
||||
for item in tree:
|
||||
if type(item) == tuple:
|
||||
item, children = item
|
||||
depth_first(children, visitor)
|
||||
visitor(item)
|
||||
|
||||
|
||||
def covercalc(tree):
|
||||
'''
|
||||
Depth first weighted average of coverage
|
||||
'''
|
||||
sum_covwt, sum_wt = 0, 0
|
||||
for item in tree:
|
||||
if type(item) == tuple:
|
||||
item, children = item
|
||||
item[COV] = covercalc(children)
|
||||
sum_wt += item[WT]
|
||||
sum_covwt += item[COV] * item[WT]
|
||||
cov = sum_covwt / sum_wt
|
||||
return cov
|
||||
|
||||
if __name__ == '__main__':
|
||||
lstc = []
|
||||
commas_to_list(lstc, ((n, ln) for n, ln in enumerate(fc2.split('\n'))))
|
||||
#pp(lstc, width=1, indent=4, compact=1)
|
||||
|
||||
#print('\n\nEXPANDED DEFAULTS\n')
|
||||
depth_first(lstc)
|
||||
#pptreefields(['NAME_HIERARCHY WEIGHT COVERAGE'.split()] + lstc)
|
||||
|
||||
print('\n\nTOP COVERAGE = %f\n' % covercalc(lstc))
|
||||
depth_first(lstc)
|
||||
pptreefields(['NAME_HIERARCHY WEIGHT COVERAGE'.split()] + lstc)
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
SPACES = 4
|
||||
class Node:
|
||||
path2node = {}
|
||||
|
||||
def add_node(self, pathname, wt, cov):
|
||||
path2node = self.path2node
|
||||
path, name = pathname.strip().rsplit('/', 1)
|
||||
node = Node(name, wt, cov)
|
||||
path2node[pathname] = node
|
||||
path2node[path].child.append(node) # Link the tree
|
||||
|
||||
def __init__(self, name="", wt=1, cov=0.0, child=None):
|
||||
if child is None:
|
||||
child = []
|
||||
self.name, self.wt, self.cov, self.child = name, wt, cov, child
|
||||
self.delta = None
|
||||
self.sum_wt = wt
|
||||
if name == "":
|
||||
# designate the top of the tree
|
||||
self.path2node[name] = self
|
||||
|
||||
|
||||
def __repr__(self, indent=0):
|
||||
name, wt, cov, delta, child = (self.name, self.wt, self.cov,
|
||||
self.delta, self.child)
|
||||
lhs = ' ' * (SPACES * indent) + "Node(%r," % name
|
||||
txt = '%-40s wt=%2g, cov=%-8.5g, delta=%-10s, child=[' \
|
||||
% (lhs, wt, cov, ('n/a' if delta is None else '%-10.7f' % delta))
|
||||
if not child:
|
||||
txt += (']),\n')
|
||||
else:
|
||||
txt += ('\n')
|
||||
for c in child:
|
||||
txt += c.__repr__(indent + 1)
|
||||
txt += (' ' * (SPACES * indent) + "]),\n")
|
||||
return txt
|
||||
|
||||
def covercalc(self):
|
||||
'''
|
||||
Depth first weighted average of coverage
|
||||
'''
|
||||
child = self.child
|
||||
if not child:
|
||||
return self.cov
|
||||
sum_covwt, sum_wt = 0, 0
|
||||
for node in child:
|
||||
nwt = node.wt
|
||||
ncov = node.covercalc()
|
||||
sum_wt += nwt
|
||||
sum_covwt += ncov * nwt
|
||||
cov = sum_covwt / sum_wt
|
||||
self.sum_wt = sum_wt
|
||||
self.cov = cov
|
||||
return cov
|
||||
|
||||
def deltacalc(self, power=1.0):
|
||||
'''
|
||||
Top down distribution of weighted residuals
|
||||
'''
|
||||
sum_wt = self.sum_wt
|
||||
self.delta = delta = (1 - self.cov) * power
|
||||
for node in self.child:
|
||||
node.deltacalc(power * node.wt / sum_wt)
|
||||
return delta
|
||||
|
||||
|
||||
def isclose(a, b, rel_tol=1e-9, abs_tol=1e-9):
|
||||
return abs(a-b) <= max( rel_tol * max(abs(a), abs(b)), abs_tol )
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
top = Node() # Add placeholder for top of tree
|
||||
add_node = top.add_node
|
||||
|
||||
add_node('/cleaning', 1, 0)
|
||||
add_node('/cleaning/house1', 40, 0)
|
||||
add_node('/cleaning/house1/bedrooms', 1, 0.25)
|
||||
add_node('/cleaning/house1/bathrooms', 1, 0)
|
||||
add_node('/cleaning/house1/bathrooms/bathroom1', 1, 0.5)
|
||||
add_node('/cleaning/house1/bathrooms/bathroom2', 1, 0)
|
||||
add_node('/cleaning/house1/bathrooms/outside_lavatory', 1, 1)
|
||||
add_node('/cleaning/house1/attic', 1, 0.75)
|
||||
add_node('/cleaning/house1/kitchen', 1, 0.1)
|
||||
add_node('/cleaning/house1/living_rooms', 1, 0)
|
||||
add_node('/cleaning/house1/living_rooms/lounge', 1, 0)
|
||||
add_node('/cleaning/house1/living_rooms/dining_room', 1, 0)
|
||||
add_node('/cleaning/house1/living_rooms/conservatory', 1, 0)
|
||||
add_node('/cleaning/house1/living_rooms/playroom', 1, 1)
|
||||
add_node('/cleaning/house1/basement', 1, 0)
|
||||
add_node('/cleaning/house1/garage', 1, 0)
|
||||
add_node('/cleaning/house1/garden', 1, 0.8)
|
||||
add_node('/cleaning/house2', 60, 0)
|
||||
add_node('/cleaning/house2/upstairs', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bedrooms', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bedrooms/suite_1', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bedrooms/suite_2', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bedrooms/bedroom_3', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bedrooms/bedroom_4', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/bathroom', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/toilet', 1, 0)
|
||||
add_node('/cleaning/house2/upstairs/attics', 1, 0.6)
|
||||
add_node('/cleaning/house2/groundfloor', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/kitchen', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/living_rooms', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/living_rooms/lounge', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/living_rooms/dining_room', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/living_rooms/conservatory', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/living_rooms/playroom', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/wet_room_&_toilet', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/garage', 1, 0)
|
||||
add_node('/cleaning/house2/groundfloor/garden', 1, 0.9)
|
||||
add_node('/cleaning/house2/groundfloor/hot_tub_suite', 1, 1)
|
||||
add_node('/cleaning/house2/basement', 1, 0)
|
||||
add_node('/cleaning/house2/basement/cellars', 1, 1)
|
||||
add_node('/cleaning/house2/basement/wine_cellar', 1, 1)
|
||||
add_node('/cleaning/house2/basement/cinema', 1, 0.75)
|
||||
|
||||
top = top.child[0] # Remove artificial top
|
||||
cover = top.covercalc()
|
||||
delta = top.deltacalc()
|
||||
print('TOP COVERAGE = %g\n' % cover)
|
||||
print(top)
|
||||
assert isclose((delta + cover), 1.0), "Top level delta + coverage should " \
|
||||
"equal 1 instead of (%f + %f)" % (delta, cover)
|
||||
|
|
@ -0,0 +1,463 @@
|
|||
'''Functional coverage tree'''
|
||||
|
||||
from itertools import chain, product
|
||||
from functools import reduce
|
||||
|
||||
|
||||
# main :: IO ()
|
||||
def main():
|
||||
'''Tabular outline serialisation of a parse tree
|
||||
decorated with computations of:
|
||||
1. Weighted coverage of each tree node.
|
||||
2. Each node's share of the total project's
|
||||
remaining work.
|
||||
'''
|
||||
columnWidths = [31, 9, 9, 9]
|
||||
delimiter = '|'
|
||||
|
||||
reportLines = REPORT.splitlines()
|
||||
columnTitles = init(columnNames(delimiter)(reportLines[0]))
|
||||
|
||||
# ------ SERIALISATION OF DECORATED PARSE TREE -------
|
||||
print(titleLine(delimiter)(columnWidths)(
|
||||
columnTitles + ['share of residue']
|
||||
))
|
||||
print(indentedLinesFromTree(' ', tabulation(columnWidths))(
|
||||
|
||||
# -------- TWO COMPUTATIONS BY TRAVERSAL ---------
|
||||
withResidueShares(1.0)(
|
||||
foldTree(weightedCoverage)(
|
||||
|
||||
# --- TREE FROM PARSE OF OUTLINE TEXT ----
|
||||
fmapTree(
|
||||
recordFromKeysDefaultsDelimiterAndLine(
|
||||
columnTitles
|
||||
)(
|
||||
[str, float, float])([
|
||||
'?', 1.0, 0.0
|
||||
])(delimiter)
|
||||
)(
|
||||
forestFromIndentLevels(
|
||||
indentLevelsFromLines(
|
||||
reportLines[1:]
|
||||
)
|
||||
)[0]
|
||||
)
|
||||
)
|
||||
)
|
||||
))
|
||||
|
||||
|
||||
# ---- WEIGHTED COVERAGE, AND SHARE OF TOTAL RESIDUE -----
|
||||
|
||||
# weightedCoverage :: Tree Dict ->
|
||||
# [Tree Dict] -> Tree Dict
|
||||
def weightedCoverage(x):
|
||||
'''The weighted coverage of a tree node,
|
||||
as a function of the weighted averages
|
||||
of its children.
|
||||
'''
|
||||
def go(xs):
|
||||
cws = [
|
||||
(r['coverage'], r['weight']) for r
|
||||
in [root(x) for x in xs]
|
||||
]
|
||||
totalWeight = reduce(lambda a, x: a + x[1], cws, 0)
|
||||
return Node(dict(
|
||||
x, **{
|
||||
'coverage': round(reduce(
|
||||
lambda a, cw: a + (cw[0] * cw[1]),
|
||||
cws, x['coverage']
|
||||
) / (totalWeight if 0 < totalWeight else 1), 5)
|
||||
}
|
||||
))(xs)
|
||||
return go
|
||||
|
||||
|
||||
# withResidueShares :: Float -> Tree Dict -> Tree Dict
|
||||
def withResidueShares(shareOfTotal):
|
||||
'''A Tree of dictionaries additionally decorated with each
|
||||
node's proportion of the total project's outstanding work.
|
||||
'''
|
||||
def go(fraction, node):
|
||||
[nodeRoot, nodeNest] = ap([root, nest])([node])
|
||||
weights = [root(x)['weight'] for x in nodeNest]
|
||||
siblingsTotal = sum(weights)
|
||||
return Node(
|
||||
insertDict('residual_share')(
|
||||
round(fraction * (1 - nodeRoot['coverage']), 5)
|
||||
)(nodeRoot)
|
||||
)(
|
||||
map(
|
||||
go,
|
||||
[fraction * (w / siblingsTotal) for w in weights],
|
||||
nodeNest
|
||||
)
|
||||
)
|
||||
return lambda tree: go(shareOfTotal, tree)
|
||||
|
||||
|
||||
# ------------------ OUTLINE TABULATION ------------------
|
||||
|
||||
# tabulation :: [Int] -> String -> Dict -> String
|
||||
def tabulation(columnWidths):
|
||||
'''Indented string representation of a node
|
||||
in a functional coverage tree.
|
||||
'''
|
||||
return lambda indent, dct: '| '.join(map(
|
||||
lambda k, w: (
|
||||
(indent if 10 < w else '') + str(dct.get(k, ''))
|
||||
).ljust(w, ' '),
|
||||
dct.keys(),
|
||||
columnWidths
|
||||
))
|
||||
|
||||
|
||||
# titleLine :: String -> [Int] -> [String] -> String
|
||||
def titleLine(delimiter):
|
||||
'''A string consisting of a spaced and delimited
|
||||
series of upper-case column titles.
|
||||
'''
|
||||
return lambda columnWidths: lambda ks: (
|
||||
delimiter + ' '
|
||||
).join(map(
|
||||
lambda k, w: k.ljust(w, ' '),
|
||||
[k.upper() for k in ks],
|
||||
columnWidths
|
||||
))
|
||||
|
||||
|
||||
# ------------ GENERIC AND REUSABLE FUNCTIONS ------------
|
||||
|
||||
# Node :: a -> [Tree a] -> Tree a
|
||||
def Node(v):
|
||||
'''Constructor for a Tree node which connects a
|
||||
value of some kind to a list of zero or
|
||||
more child trees.
|
||||
'''
|
||||
return lambda xs: {'type': 'Tree', 'root': v, 'nest': xs}
|
||||
|
||||
|
||||
# ap (<*>) :: [(a -> b)] -> [a] -> [b]
|
||||
def ap(fs):
|
||||
'''The application of each of a list of functions,
|
||||
to each of a list of values.
|
||||
'''
|
||||
def go(xs):
|
||||
return [
|
||||
f(x) for (f, x)
|
||||
in product(fs, xs)
|
||||
]
|
||||
return go
|
||||
|
||||
|
||||
# columnNames :: String -> String -> [String]
|
||||
def columnNames(delimiter):
|
||||
'''A list of lower-case keys derived from
|
||||
a header line and a delimiter character.
|
||||
'''
|
||||
return compose(
|
||||
fmapList(compose(toLower, strip)),
|
||||
splitOn(delimiter)
|
||||
)
|
||||
|
||||
|
||||
# compose :: ((a -> a), ...) -> (a -> a)
|
||||
def compose(*fs):
|
||||
'''Composition, from right to left,
|
||||
of a series of functions.
|
||||
'''
|
||||
return lambda x: reduce(
|
||||
lambda a, f: f(a),
|
||||
fs[::-1], x
|
||||
)
|
||||
|
||||
|
||||
# concatMap :: (a -> [b]) -> [a] -> [b]
|
||||
def concatMap(f):
|
||||
'''A concatenated list over which a function has been mapped.
|
||||
The list monad can be derived by using a function f which
|
||||
wraps its output in a list,
|
||||
(using an empty list to represent computational failure).
|
||||
'''
|
||||
def go(xs):
|
||||
return chain.from_iterable(map(f, xs))
|
||||
return go
|
||||
|
||||
|
||||
# div :: Int -> Int -> Int
|
||||
def div(x):
|
||||
'''Integer division.'''
|
||||
return lambda y: x // y
|
||||
|
||||
|
||||
# first :: (a -> b) -> ((a, c) -> (b, c))
|
||||
def first(f):
|
||||
'''A simple function lifted to a function over a tuple,
|
||||
with f applied only the first of two values.
|
||||
'''
|
||||
return lambda xy: (f(xy[0]), xy[1])
|
||||
|
||||
|
||||
# flip :: (a -> b -> c) -> b -> a -> c
|
||||
def flip(f):
|
||||
'''The (curried or uncurried) function f with its
|
||||
arguments reversed.
|
||||
'''
|
||||
return lambda a: lambda b: f(b)(a)
|
||||
|
||||
|
||||
# fmapList :: (a -> b) -> [a] -> [b]
|
||||
def fmapList(f):
|
||||
'''fmap over a list.
|
||||
f lifted to a function over a list.
|
||||
'''
|
||||
return lambda xs: [f(x) for x in xs]
|
||||
|
||||
|
||||
# fmapTree :: (a -> b) -> Tree a -> Tree b
|
||||
def fmapTree(f):
|
||||
'''A new tree holding the results of
|
||||
an application of f to each root in
|
||||
the existing tree.
|
||||
'''
|
||||
def go(x):
|
||||
return Node(
|
||||
f(x['root'])
|
||||
)([go(v) for v in x['nest']])
|
||||
return go
|
||||
|
||||
|
||||
# foldTree :: (a -> [b] -> b) -> Tree a -> b
|
||||
def foldTree(f):
|
||||
'''The catamorphism on trees. A summary
|
||||
value defined by a depth-first fold.
|
||||
'''
|
||||
def go(node):
|
||||
return f(root(node))([
|
||||
go(x) for x in nest(node)
|
||||
])
|
||||
return go
|
||||
|
||||
|
||||
# forestFromIndentLevels :: [(Int, a)] -> [Tree a]
|
||||
def forestFromIndentLevels(tuples):
|
||||
'''A list of trees derived from a list of values paired
|
||||
with integers giving their levels of indentation.
|
||||
'''
|
||||
def go(xs):
|
||||
if xs:
|
||||
intIndent, v = xs[0]
|
||||
firstTreeLines, rest = span(
|
||||
lambda x: intIndent < x[0]
|
||||
)(xs[1:])
|
||||
return [Node(v)(go(firstTreeLines))] + go(rest)
|
||||
else:
|
||||
return []
|
||||
return go(tuples)
|
||||
|
||||
|
||||
# fst :: (a, b) -> a
|
||||
def fst(tpl):
|
||||
'''First member of a pair.'''
|
||||
return tpl[0]
|
||||
|
||||
|
||||
# indentLevelsFromLines :: [String] -> [(Int, String)]
|
||||
def indentLevelsFromLines(xs):
|
||||
'''Each input line stripped of leading
|
||||
white space, and tupled with a preceding integer
|
||||
giving its level of indentation from 0 upwards.
|
||||
'''
|
||||
indentTextPairs = list(map(
|
||||
compose(first(len), span(isSpace)),
|
||||
xs
|
||||
))
|
||||
indentUnit = min(concatMap(
|
||||
lambda x: [x[0]] if x[0] else []
|
||||
)(indentTextPairs))
|
||||
return list(map(
|
||||
first(flip(div)(indentUnit)),
|
||||
indentTextPairs
|
||||
))
|
||||
|
||||
|
||||
# indentedLinesFromTree :: String -> (String -> a -> String) ->
|
||||
# [Tree a] -> String
|
||||
def indentedLinesFromTree(strTab, f):
|
||||
'''An indented line rendering of a tree, in which
|
||||
the function f stringifies a root value.
|
||||
'''
|
||||
def go(indent):
|
||||
return lambda node: [f(indent, node['root'])] + list(
|
||||
concatMap(
|
||||
go(strTab + indent)
|
||||
)(node['nest'])
|
||||
)
|
||||
return lambda tree: '\n'.join(go('')(tree))
|
||||
|
||||
|
||||
# init :: [a] -> [a]
|
||||
def init(xs):
|
||||
'''A list containing all the elements
|
||||
of xs except the last.
|
||||
'''
|
||||
return xs[:-1]
|
||||
|
||||
|
||||
# insertDict :: String -> a -> Dict -> Dict
|
||||
def insertDict(k):
|
||||
'''A new dictionary updated with a (k, v) pair.'''
|
||||
def go(v, dct):
|
||||
return dict(dct, **{k: v})
|
||||
return lambda v: lambda dct: go(v, dct)
|
||||
|
||||
|
||||
# isSpace :: Char -> Bool
|
||||
# isSpace :: String -> Bool
|
||||
def isSpace(s):
|
||||
'''True if s is not empty, and
|
||||
contains only white space.
|
||||
'''
|
||||
return s.isspace()
|
||||
|
||||
|
||||
# lt (<) :: Ord a => a -> a -> Bool
|
||||
def lt(x):
|
||||
'''True if x < y.'''
|
||||
return lambda y: (x < y)
|
||||
|
||||
|
||||
# nest :: Tree a -> [Tree a]
|
||||
def nest(t):
|
||||
'''Accessor function for children of tree node.'''
|
||||
return t['nest'] if 'nest' in t else None
|
||||
|
||||
|
||||
# recordFromKeysDefaultsAndLine :: String ->
|
||||
# { name :: String, weight :: Float, completion :: Float }
|
||||
def recordFromKeysDefaultsDelimiterAndLine(columnTitles):
|
||||
'''A dictionary of key-value pairs, derived from a
|
||||
delimited string, together with ordered lists of
|
||||
key-names, types, default values, and a delimiter.
|
||||
'''
|
||||
return lambda ts: lambda vs: lambda delim: lambda s: dict(
|
||||
map(
|
||||
lambda k, t, v, x: (k, t(x) if x else v),
|
||||
columnTitles, ts, vs,
|
||||
map(strip, splitOn(delim)(s))
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# root :: Tree a -> a
|
||||
def root(t):
|
||||
'''Accessor function for data of tree node.'''
|
||||
return t['root'] if 'root' in t else None
|
||||
|
||||
|
||||
# strip :: String -> String
|
||||
def strip(s):
|
||||
'''A copy of s without any leading or trailling
|
||||
white space.
|
||||
'''
|
||||
return s.strip()
|
||||
|
||||
|
||||
# span :: (a -> Bool) -> [a] -> ([a], [a])
|
||||
def span(p):
|
||||
'''The longest (possibly empty) prefix of xs
|
||||
that contains only elements satisfying p,
|
||||
tupled with the remainder of xs.
|
||||
span p xs is equivalent to (takeWhile p xs, dropWhile p xs).
|
||||
'''
|
||||
def match(ab):
|
||||
b = ab[1]
|
||||
return not b or not p(b[0])
|
||||
|
||||
def f(ab):
|
||||
a, b = ab
|
||||
return a + [b[0]], b[1:]
|
||||
|
||||
def go(xs):
|
||||
return until(match)(f)(([], xs))
|
||||
return go
|
||||
|
||||
|
||||
# splitOn :: String -> String -> [String]
|
||||
def splitOn(pat):
|
||||
'''A list of the strings delimited by
|
||||
instances of a given pattern in s.
|
||||
'''
|
||||
return lambda xs: (
|
||||
xs.split(pat) if isinstance(xs, str) else None
|
||||
)
|
||||
|
||||
|
||||
# toLower :: String -> String
|
||||
def toLower(s):
|
||||
'''String in lower case.'''
|
||||
return s.lower()
|
||||
|
||||
|
||||
# until :: (a -> Bool) -> (a -> a) -> a -> a
|
||||
def until(p):
|
||||
'''The result of repeatedly applying f until p holds.
|
||||
The initial seed value is x.
|
||||
'''
|
||||
def go(f):
|
||||
def g(x):
|
||||
v = x
|
||||
while not p(v):
|
||||
v = f(v)
|
||||
return v
|
||||
return g
|
||||
return go
|
||||
|
||||
|
||||
# MAIN ----------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
REPORT = '''NAME_HIERARCHY |WEIGHT |COVERAGE |
|
||||
cleaning | | |
|
||||
house1 |40 | |
|
||||
bedrooms | |0.25 |
|
||||
bathrooms | | |
|
||||
bathroom1 | |0.5 |
|
||||
bathroom2 | | |
|
||||
outside_lavatory | |1 |
|
||||
attic | |0.75 |
|
||||
kitchen | |0.1 |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | |1 |
|
||||
basement | | |
|
||||
garage | | |
|
||||
garden | |0.8 |
|
||||
house2 |60 | |
|
||||
upstairs | | |
|
||||
bedrooms | | |
|
||||
suite_1 | | |
|
||||
suite_2 | | |
|
||||
bedroom_3 | | |
|
||||
bedroom_4 | | |
|
||||
bathroom | | |
|
||||
toilet | | |
|
||||
attics | |0.6 |
|
||||
groundfloor | | |
|
||||
kitchen | | |
|
||||
living_rooms | | |
|
||||
lounge | | |
|
||||
dining_room | | |
|
||||
conservatory | | |
|
||||
playroom | | |
|
||||
wet_room_&_toilet | | |
|
||||
garage | | |
|
||||
garden | |0.9 |
|
||||
hot_tub_suite | |1 |
|
||||
basement | | |
|
||||
cellars | |1 |
|
||||
wine_cellar | |1 |
|
||||
cinema | |0.75 |'''
|
||||
main()
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
#lang racket/base
|
||||
(require racket/list racket/string racket/match racket/format racket/file)
|
||||
|
||||
(struct Coverage (name weight coverage weighted-coverage children) #:transparent #:mutable)
|
||||
|
||||
;; -| read/parse |------------------------------------------------------------------------------------
|
||||
(define (build-hierarchies parsed-lines)
|
||||
(define inr
|
||||
(match-lambda
|
||||
['() (values null null)]
|
||||
[`((,head-indent . ,C) ,tail-lines ...)
|
||||
(define child? (match-lambda [(cons i _) #:when (> i head-indent) #t] [_ #f]))
|
||||
(define-values (chlds rels) (splitf-at tail-lines child?))
|
||||
(define-values (rels-tree rels-rem) (inr rels))
|
||||
(values (cons (struct-copy Coverage C (children (build-hierarchies chlds))) rels-tree)
|
||||
rels-rem)]))
|
||||
(define-values (hierarchies remaining-lines) (inr parsed-lines))
|
||||
hierarchies)
|
||||
|
||||
(define report-line->indent.c/e-line
|
||||
(match-lambda
|
||||
[(regexp #px"^( *)([^ ]*) *\\| *([^ ]*) *\\| *([^ ]*) *\\|$"
|
||||
(list _
|
||||
(app string-length indent-length)
|
||||
name
|
||||
(or (and (not "") (app string->number wght)) (app (λ (x) 1) wght))
|
||||
(or (and (not "") (app string->number cvrg)) (app (λ (x) 0) cvrg))))
|
||||
(cons indent-length (Coverage name wght cvrg 0 #f))]))
|
||||
|
||||
(define (report->indent.c/e-list rprt)
|
||||
(map report-line->indent.c/e-line (drop (string-split rprt "\n") 1)))
|
||||
|
||||
;; -| evaluate |--------------------------------------------------------------------------------------
|
||||
(define find-wght-cvrg
|
||||
(match-lambda
|
||||
[(and e (Coverage _ w c _ '())) (struct-copy Coverage e (weighted-coverage (* w c)))]
|
||||
[(and e (Coverage _ _ _ _ `(,(app find-wght-cvrg (and cdn+ (Coverage _ c-ws _ c-w/cs _))) ...)))
|
||||
(define chld-wghtd-avg (for/sum ((w (in-list c-ws)) (w/c (in-list c-w/cs))) (* w w/c)))
|
||||
(struct-copy Coverage e (weighted-coverage (/ chld-wghtd-avg (apply + c-ws))) (children cdn+))]))
|
||||
|
||||
;; -| printing |--------------------------------------------------------------------------------------
|
||||
(define max-description-length
|
||||
(match-lambda
|
||||
[(Coverage (app string-length name-length) _ _ _
|
||||
(list (app max-description-length children-lengths) ...))
|
||||
(apply max name-length (map add1 children-lengths))]))
|
||||
|
||||
(define (~a/right w x)
|
||||
(~a x #:width w #:align 'right))
|
||||
|
||||
(define (~a/decimal n dec-dgts)
|
||||
(~a/right (+ dec-dgts 3) (if (zero? n) "" (real->decimal-string n dec-dgts))))
|
||||
|
||||
(define (print-coverage-tree tree)
|
||||
(define mdl (max-description-length tree))
|
||||
(printf "| ~a |WEIGT| COVER |WGHTD CVRG|~%" (~a "NAME" #:width mdl #:align 'center))
|
||||
(let inr ((depth 0) (tree tree))
|
||||
(unless (null? tree)
|
||||
(match tree
|
||||
[(Coverage name w c w/c chlds)
|
||||
(printf "| ~a | ~a | ~a | ~a |~%"
|
||||
(~a (string-append (make-string depth #\space) name) #:width mdl)
|
||||
(~a/right 3 w) (~a/decimal c 2) (~a/decimal w/c 5))
|
||||
(for ((c chlds)) (inr (add1 depth) c))]))))
|
||||
|
||||
;; ---------------------------------------------------------------------------------------------------
|
||||
(module+ main
|
||||
;; data/functional-coverage.txt contains a verbatim copy of
|
||||
;; the table in the task's description
|
||||
(for-each
|
||||
(compose print-coverage-tree find-wght-cvrg)
|
||||
(build-hierarchies (report->indent.c/e-list (file->string "data/functional-coverage.txt")))))
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
sub walktree ($data) {
|
||||
my (@parts, $cnt);
|
||||
|
||||
while ($data ~~ m:nth(++$cnt)/$<head>=[(\s*) \N+\n ] # split off one level as 'head' (or terminal 'leaf')
|
||||
$<body>=[[$0 \s+ \N+\n]*]/ ) { # next sub-level is 'body' (defined by extra depth of indentation)
|
||||
|
||||
my ($head, $body) = ($<head>, $<body>);
|
||||
$head ~~ /'|' $<weight>=[\S*] \s* '|' $<coverage>=[\S*]/; # save values of weight and coverage (if any) for later
|
||||
|
||||
my ($w, $wsum) = (0, 0);
|
||||
$head ~= .[0],
|
||||
$w += .[1],
|
||||
$wsum += .[1] * .[2]
|
||||
for walktree $body;
|
||||
|
||||
my $weight = (~$<weight> or 1).fmt('%-8s');
|
||||
my $coverage = $w == 0
|
||||
?? (~$<coverage> or 0).fmt('%-10s')
|
||||
!! ($wsum/$w) .fmt('%-10.2g');
|
||||
@parts.push: [$head.subst(/'|' \N+/, "|$weight|$coverage|"), $weight, $coverage ];
|
||||
}
|
||||
return @parts;
|
||||
}
|
||||
|
||||
(say .[0] for walktree $_) given
|
||||
|
||||