|  | 
|  | 1 | +<# | 
|  | 2 | +.SYNOPSIS | 
|  | 3 | +Given partial information about a jigsaw puzzle, add the missing pieces. | 
|  | 4 | +
 | 
|  | 5 | +.DESCRIPTION | 
|  | 6 | +Calculate properties of a jigsaw puzzle with given information if possible. | 
|  | 7 | +If not possible due to insufficient or incorrect information, the user should be notified. | 
|  | 8 | +Read instructions for more information and example. | 
|  | 9 | +#> | 
|  | 10 | + | 
|  | 11 | +enum Format { | 
|  | 12 | +    Unknown | 
|  | 13 | +    Portrait | 
|  | 14 | +    Square | 
|  | 15 | +    Landscape | 
|  | 16 | +} | 
|  | 17 | + | 
|  | 18 | +class JigsawPuzzle { | 
|  | 19 | +    [int]$Pieces | 
|  | 20 | +    [int]$Border | 
|  | 21 | +    [int]$Inside | 
|  | 22 | +    [int]$Rows | 
|  | 23 | +    [int]$Columns | 
|  | 24 | +    [double]$AspectRatio | 
|  | 25 | +    [Format]$Format | 
|  | 26 | + | 
|  | 27 | +    GetData([PSCustomObject]$partialData) | 
|  | 28 | +    { | 
|  | 29 | +        if ($partialData.PSObject.Properties.Name -contains "AspectRatio") { | 
|  | 30 | +            $this.ProcessDataFromRatio($partialData) | 
|  | 31 | +        } elseif ($partialData.PSObject.Properties.Name -contains "Format") { | 
|  | 32 | +            $this.ProcessDataFromFormat($partialData) | 
|  | 33 | +        } else { | 
|  | 34 | +            throw "insufficient data" | 
|  | 35 | +        } | 
|  | 36 | +    } | 
|  | 37 | + | 
|  | 38 | +    FillFromRowsAndColumns() | 
|  | 39 | +    { | 
|  | 40 | +        $this.Pieces = $this.Rows * $this.Columns | 
|  | 41 | +        $this.Border = 2*$this.Rows + 2*$this.Columns - 4 | 
|  | 42 | +        $this.Inside = $this.Pieces - $this.Border | 
|  | 43 | +        $this.AspectRatio = $this.Columns / $this.Rows | 
|  | 44 | +    } | 
|  | 45 | + | 
|  | 46 | +    [int[]]FindRowsAndColumn() { | 
|  | 47 | +        $a, $b = 0, 0 | 
|  | 48 | +        for ($i = 1; $i -le [Math]::Sqrt($this.Pieces); $i++) { | 
|  | 49 | +            if (($i * $i * $this.AspectRatio) -eq $this.Pieces) { | 
|  | 50 | +                $a, $b = $i, ($i * $this.AspectRatio) | 
|  | 51 | +                break | 
|  | 52 | +            } | 
|  | 53 | +        } | 
|  | 54 | +        return $this.Format -eq [Format]::Landscape ? [Math]::Min($a,$b), [Math]::Max($a,$b)  | 
|  | 55 | +                                                    : [Math]::Max($a,$b), [Math]::Min($a,$b)  | 
|  | 56 | +    } | 
|  | 57 | + | 
|  | 58 | +    ProcessDataFromRatio([PSCustomObject]$partialData) | 
|  | 59 | +    { | 
|  | 60 | +        $properties = $partialData.PSObject.Properties.Name | 
|  | 61 | +        $this.AspectRatio = $partialData.AspectRatio | 
|  | 62 | +        $this.Format = switch ($true) { | 
|  | 63 | +            ($partialData.AspectRatio -gt 1) { [Format]::Landscape } | 
|  | 64 | +            ($partialData.AspectRatio -lt 1) { [Format]::Portrait } | 
|  | 65 | +            Default {[Format]::Square} | 
|  | 66 | +        } | 
|  | 67 | + | 
|  | 68 | +        if ($properties -contains "Pieces") { | 
|  | 69 | +            $this.Pieces = $partialData.Pieces | 
|  | 70 | +            $this.Rows, $this.Columns = $this.FindRowsAndColumn() | 
|  | 71 | +        } elseif ($properties -contains "Border") { | 
|  | 72 | +            $this.Border = $partialData.Border | 
|  | 73 | +            $this.Rows = ($this.Border + 4) / (2 * (1 + $this.AspectRatio)) | 
|  | 74 | +            $this.Columns = $this.Rows * $this.AspectRatio | 
|  | 75 | +        } elseif ($properties -contains "Inside") { | 
|  | 76 | +            $this.Inside = $partialData.Inside | 
|  | 77 | +            $r = $this.AspectRatio | 
|  | 78 | +            $i = $this.Inside | 
|  | 79 | + | 
|  | 80 | +            $discriminant = [Math]::Pow((2 * $r + 2), 2) - (16 * $r) + (4 * $r * $i) | 
|  | 81 | +            $rows1 = ((2 * $r + 2) + [Math]::Sqrt($discriminant)) / (2 * $r) | 
|  | 82 | +            $rows2 = ((2 * $r + 2) - [Math]::Sqrt($discriminant)) / (2 * $r) | 
|  | 83 | + | 
|  | 84 | +            $this.Rows = @($rows1, $rows2) | Where-Object { $_ -gt 0 -and [Math]::Round($_) -eq $_ } | Select-Object -First 1 | 
|  | 85 | +            $this.Columns = $r * $this.Rows | 
|  | 86 | +        } elseif ($properties -contains "Rows") { | 
|  | 87 | +            $this.Rows = $partialData.Rows | 
|  | 88 | +            $this.Columns = $this.AspectRatio * $this.Rows | 
|  | 89 | +        } elseif ($properties -contains "Columns") { | 
|  | 90 | +            $this.Columns = $partialData.Columns | 
|  | 91 | +            $this.Rows = $this.Columns / $this.AspectRatio | 
|  | 92 | +        } | 
|  | 93 | +        $this.FillFromRowsAndColumns() | 
|  | 94 | +    } | 
|  | 95 | + | 
|  | 96 | +    ProcessDataFromFormat([PSCustomObject]$partialData) | 
|  | 97 | +    { | 
|  | 98 | +        $this.Format = $partialData.Format | 
|  | 99 | +        $properties = $partialData.PSObject.Properties.Name | 
|  | 100 | +        if ($this.Format -eq [Format]::Square) { | 
|  | 101 | +            $this.AspectRatio = 1.0 | 
|  | 102 | +            if ($properties -contains "Pieces") { | 
|  | 103 | +                $this.Rows = $this.Columns = [Math]::Sqrt($partialData.Pieces) | 
|  | 104 | +            } elseif ($properties -contains "Rows") { | 
|  | 105 | +                $this.Rows = $partialData.Rows | 
|  | 106 | +                $this.Columns = $this.Rows | 
|  | 107 | +            } elseif ($properties -contains "Columns") { | 
|  | 108 | +                $this.Columns = $partialData.Columns | 
|  | 109 | +                $this.Rows = $this.Columns | 
|  | 110 | +            } elseif ($properties -contains "Border") { | 
|  | 111 | +                $this.Rows = $this.Columns = ($partialData.Border + 4) / 4 | 
|  | 112 | +            } elseif ($properties -contains "Inside") { | 
|  | 113 | +                $this.Rows = $this.Columns = [Math]::Sqrt($partialData.Inside) + 2 | 
|  | 114 | +            } else { | 
|  | 115 | +                throw "bad data" | 
|  | 116 | +            } | 
|  | 117 | +        } else { | 
|  | 118 | +            $otherProperties = ($properties | Where-Object { $_ -ne "Format"}).Count | 
|  | 119 | +            if ($otherProperties -lt 2) { | 
|  | 120 | +                throw "insufficient data" | 
|  | 121 | +            } | 
|  | 122 | +            $pairs = $this.GenerateRowsAndColumnsFromFormat([PSCustomObject]$partialData) | 
|  | 123 | +            foreach ($pair in $pairs) { | 
|  | 124 | +                $tempPieces = $pair[0] * $pair[1] | 
|  | 125 | +                $tempBorder = 2*$pair[0] + 2*$pair[1] - 4 | 
|  | 126 | +                if ($tempPieces -eq $partialData.Pieces -and $tempBorder -eq $partialData.Border) { | 
|  | 127 | +                    if ($partialData.Format -eq [Format]::Landscape) { | 
|  | 128 | +                        $this.Rows = [Math]::Min($pair[0], $pair[1]) | 
|  | 129 | +                        $this.Columns = [Math]::Max($pair[0], $pair[1]) | 
|  | 130 | +                    } else { | 
|  | 131 | +                        $this.Rows = [Math]::Max($pair[0], $pair[1]) | 
|  | 132 | +                        $this.Columns = [Math]::Min($pair[0], $pair[1]) | 
|  | 133 | +                    } | 
|  | 134 | +                } | 
|  | 135 | +            } | 
|  | 136 | + | 
|  | 137 | +        } | 
|  | 138 | +        $this.FillFromRowsAndColumns() | 
|  | 139 | +        $this.ValidateGivenData($partialData) | 
|  | 140 | +    } | 
|  | 141 | + | 
|  | 142 | +    [object[]]GenerateRowsAndColumnsFromFormat([PSCustomObject]$partialData) { | 
|  | 143 | +        $properties = $partialData.PSObject.Properties.Name | 
|  | 144 | +        $pairs = @() | 
|  | 145 | +        if ($properties -contains "Pieces") { | 
|  | 146 | +            $this.Pieces = $partialData.Pieces | 
|  | 147 | +            for ($r = 1; $r -le [Math]::Sqrt($this.Pieces); $r++) { | 
|  | 148 | +                for ($c = $r; $c -le $this.Pieces; $c++) { | 
|  | 149 | +                    if ($r * $c -eq $this.Pieces) { | 
|  | 150 | +                        $pairs += ,@($c, $r) | 
|  | 151 | +                    } | 
|  | 152 | +                } | 
|  | 153 | +            } | 
|  | 154 | +        }elseif ($properties -contains "Border") { | 
|  | 155 | +            $this.Border = $partialData.Border | 
|  | 156 | +            $sum = ($this.Border + 4) / 2 | 
|  | 157 | +            for ($c = 1; $c -lt ($sum - 1); $c++) { | 
|  | 158 | +                $pairs += ,@($c, ($sum - $c)) | 
|  | 159 | +            } | 
|  | 160 | +        } | 
|  | 161 | +        return $pairs | 
|  | 162 | +    } | 
|  | 163 | + | 
|  | 164 | +    ValidateGivenData([PSCustomObject]$partialData) | 
|  | 165 | +    { | 
|  | 166 | +        foreach ($property in $partialData.psobject.Properties) { | 
|  | 167 | +            if ($property.Value -ne $this.PSObject.Properties[$property.Name].Value) { | 
|  | 168 | +                throw "contradictory data" | 
|  | 169 | +            } | 
|  | 170 | +        } | 
|  | 171 | +    } | 
|  | 172 | +} | 
0 commit comments