├── vendor ├── composer │ ├── installed.json │ ├── autoload_classmap.php │ ├── autoload_namespaces.php │ ├── autoload_psr4.php │ ├── autoload_static.php │ ├── LICENSE │ ├── autoload_real.php │ └── ClassLoader.php └── autoload.php ├── joko-dm └── Datamining │ ├── data │ ├── dummy.csv │ ├── bermain-golf-numerik.csv │ ├── bermain-golf-nominal.csv │ ├── pembelian-komputer.csv │ └── breast-cancer.csv │ ├── Klasifikasi │ ├── Classifier.php │ ├── KNearestNeighbors.php │ ├── DecisionTree │ │ └── DecisionTreeLeaf.php │ ├── NaiveBayes.php │ └── DecisionTree.php │ ├── Exception │ ├── InvalidOperationException.php │ ├── NormalizerException.php │ ├── DatasetException.php │ ├── LibsvmCommandException.php │ ├── SerializeException.php │ ├── MatrixException.php │ ├── FileException.php │ └── InvalidArgumentException.php │ ├── Math │ ├── Distance.php │ ├── Kernel.php │ ├── Product.php │ ├── Distance │ │ ├── Manhattan.php │ │ └── Euclidean.php │ ├── Kernel │ │ └── RBF.php │ ├── Statistic │ │ ├── Variance.php │ │ ├── Correlation.php │ │ ├── Gaussian.php │ │ ├── StandardDeviation.php │ │ ├── Mean.php │ │ ├── ANOVA.php │ │ └── Covariance.php │ ├── Comparison.php │ ├── Set.php │ ├── Matrix.php │ └── LinearAlgebra │ │ └── LUDecomposition.php │ ├── Dataset │ ├── Dataset.php │ ├── Demo │ │ ├── DummyDataset.php │ │ ├── BermainGolf.php │ │ ├── GolfNominal.php │ │ ├── PembelianKomputer.php │ │ └── BreastCancer.php │ ├── ArrayDataset.php │ ├── FilesDataset.php │ └── CsvDataset.php │ ├── Estimator.php │ └── Helper │ ├── Trainable.php │ └── Predictable.php ├── public ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── css │ ├── bootstrap-reboot.min.css │ ├── bootstrap-reboot.css │ ├── bootstrap-reboot.min.css.map │ └── bootstrap-grid.min.css ├── composer.json └── README.md /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | [] 2 | -------------------------------------------------------------------------------- /joko-dm/Datamining/data/dummy.csv: -------------------------------------------------------------------------------- 1 | x,y,kelas 2 | 1.2,2.3,A 3 | 2.5,4.6,A 4 | 4.0,1.0,B 5 | 5.6,1.2,B 6 | 6.0,3.5,B 7 | -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jokosuntoro/php-datamining/HEAD/public/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jokosuntoro/php-datamining/HEAD/public/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /public/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jokosuntoro/php-datamining/HEAD/public/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/joko-dm'), 10 | ); 11 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Distance.php: -------------------------------------------------------------------------------- 1 | $value) { 16 | if (is_numeric($value) && is_numeric($b[$index])) { 17 | $product += $value * $b[$index]; 18 | } 19 | } 20 | 21 | return $product; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Dataset/Demo/BreastCancer.php: -------------------------------------------------------------------------------- 1 | samples = array_merge($this->samples, $samples); 26 | $this->targets = array_merge($this->targets, $targets); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Distance/Manhattan.php: -------------------------------------------------------------------------------- 1 | gamma = $gamma; 20 | } 21 | 22 | /** 23 | * @param array $a 24 | * @param array $b 25 | */ 26 | public function compute($a, $b): float 27 | { 28 | $score = 2 * Product::scalar($a, $b); 29 | $squares = Product::scalar($a, $a) + Product::scalar($b, $b); 30 | 31 | return exp(-$this->gamma * ($squares - $score)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Distance/Euclidean.php: -------------------------------------------------------------------------------- 1 | $val) { 20 | $distance += ($val - $b[$i]) ** 2; 21 | } 22 | 23 | return sqrt((float) $distance); 24 | } 25 | 26 | /** 27 | * Square of Euclidean distance 28 | */ 29 | public function sqDistance(array $a, array $b): float 30 | { 31 | return $this->distance($a, $b) ** 2; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Helper/Predictable.php: -------------------------------------------------------------------------------- 1 | predictSample($samples); 18 | } 19 | 20 | $predicted = []; 21 | foreach ($samples as $index => $sample) { 22 | $predicted[$index] = $this->predictSample($sample); 23 | } 24 | 25 | return $predicted; 26 | } 27 | 28 | /** 29 | * @param array $sample 30 | * 31 | * @return mixed 32 | */ 33 | abstract protected function predictSample(array $sample); 34 | } 35 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Statistic/Variance.php: -------------------------------------------------------------------------------- 1 | samples = $samples; 29 | $this->targets = $targets; 30 | } 31 | 32 | public function getSamples(): array 33 | { 34 | return $this->samples; 35 | } 36 | 37 | public function getTargets(): array 38 | { 39 | return $this->targets; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'jokodm\\' => 7, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'jokodm\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/joko-dm', 20 | ), 21 | ); 22 | 23 | public static function getInitializer(ClassLoader $loader) 24 | { 25 | return \Closure::bind(function () use ($loader) { 26 | $loader->prefixLengthsPsr4 = ComposerStaticInit58447561b3afd1a9935214f6e1ae89d7::$prefixLengthsPsr4; 27 | $loader->prefixDirsPsr4 = ComposerStaticInit58447561b3afd1a9935214f6e1ae89d7::$prefixDirsPsr4; 28 | 29 | }, null, ClassLoader::class); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Dataset/FilesDataset.php: -------------------------------------------------------------------------------- 1 | scanRootPath($rootPath); 18 | } 19 | 20 | private function scanRootPath(string $rootPath): void 21 | { 22 | foreach (glob($rootPath.DIRECTORY_SEPARATOR.'*', GLOB_ONLYDIR) as $dir) { 23 | $this->scanDir($dir); 24 | } 25 | } 26 | 27 | private function scanDir(string $dir): void 28 | { 29 | $target = basename($dir); 30 | 31 | foreach (array_filter(glob($dir.DIRECTORY_SEPARATOR.'*'), 'is_file') as $file) { 32 | $this->samples[] = [file_get_contents($file)]; 33 | $this->targets[] = $target; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Comparison.php: -------------------------------------------------------------------------------- 1 | ': 18 | return $a > $b; 19 | case '>=': 20 | return $a >= $b; 21 | case '=': 22 | case '==': 23 | return $a == $b; 24 | case '===': 25 | return $a === $b; 26 | case '<=': 27 | return $a <= $b; 28 | case '<': 29 | return $a < $b; 30 | case '!=': 31 | case '<>': 32 | return $a != $b; 33 | case '!==': 34 | return $a !== $b; 35 | default: 36 | throw InvalidArgumentException::invalidOperator($operator); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Statistic/Correlation.php: -------------------------------------------------------------------------------- 1 | mean = $mean; 22 | $this->std = $std; 23 | } 24 | 25 | /** 26 | * Returns probability density of the given $value 27 | * 28 | * @return float|int 29 | */ 30 | public function pdf(float $value) 31 | { 32 | // Calculate the probability density by use of normal/Gaussian distribution 33 | // Ref: https://en.wikipedia.org/wiki/Normal_distribution 34 | $std2 = $this->std ** 2; 35 | $mean = $this->mean; 36 | 37 | return exp(-(($value - $mean) ** 2) / (2 * $std2)) / sqrt(2 * $std2 * pi()); 38 | } 39 | 40 | /** 41 | * Returns probability density value of the given $value based on 42 | * given standard deviation and the mean 43 | */ 44 | public static function distributionPdf(float $mean, float $std, float $value): float 45 | { 46 | $normal = new self($mean, $std); 47 | 48 | return $normal->pdf($value); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Dataset/CsvDataset.php: -------------------------------------------------------------------------------- 1 | columnNames = array_slice($data, 0, $features); 27 | } else { 28 | $this->columnNames = range(0, $features - 1); 29 | } 30 | 31 | $samples = $targets = []; 32 | while (($data = fgetcsv($handle, $maxLineLength, $delimiter)) !== false) { 33 | $samples[] = array_slice($data, 0, $features); 34 | $targets[] = $data[$features]; 35 | } 36 | 37 | fclose($handle); 38 | 39 | parent::__construct($samples, $targets); 40 | } 41 | 42 | public function getColumnNames(): array 43 | { 44 | return $this->columnNames; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Statistic/StandardDeviation.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 27 | if ($useStaticLoader) { 28 | require_once __DIR__ . '/autoload_static.php'; 29 | 30 | call_user_func(\Composer\Autoload\ComposerStaticInit58447561b3afd1a9935214f6e1ae89d7::getInitializer($loader)); 31 | } else { 32 | $map = require __DIR__ . '/autoload_namespaces.php'; 33 | foreach ($map as $namespace => $path) { 34 | $loader->set($namespace, $path); 35 | } 36 | 37 | $map = require __DIR__ . '/autoload_psr4.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->setPsr4($namespace, $path); 40 | } 41 | 42 | $classMap = require __DIR__ . '/autoload_classmap.php'; 43 | if ($classMap) { 44 | $loader->addClassMap($classMap); 45 | } 46 | } 47 | 48 | $loader->register(true); 49 | 50 | return $loader; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Klasifikasi/KNearestNeighbors.php: -------------------------------------------------------------------------------- 1 | k = $k; 36 | $this->samples = []; 37 | $this->targets = []; 38 | $this->distanceMetric = $distanceMetric; 39 | } 40 | 41 | /** 42 | * @return mixed 43 | */ 44 | protected function predictSample(array $sample) 45 | { 46 | $distances = $this->kNeighborsDistances($sample); 47 | 48 | $predictions = array_combine(array_values($this->targets), array_fill(0, count($this->targets), 0)); 49 | 50 | foreach ($distances as $index => $distance) { 51 | ++$predictions[$this->targets[$index]]; 52 | } 53 | 54 | arsort($predictions); 55 | reset($predictions); 56 | 57 | return key($predictions); 58 | } 59 | 60 | /** 61 | * @throws \Exception\InvalidArgumentException 62 | */ 63 | private function kNeighborsDistances(array $sample): array 64 | { 65 | $distances = []; 66 | 67 | foreach ($this->samples as $index => $neighbor) { 68 | $distances[$index] = $this->distanceMetric->distance($sample, $neighbor); 69 | } 70 | 71 | asort($distances); 72 | 73 | return array_slice($distances, 0, $this->k, true); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Exception/InvalidArgumentException.php: -------------------------------------------------------------------------------- 1 | getHTML(); 80 | } 81 | 82 | public function evaluate(array $record): bool 83 | { 84 | $recordField = $record[$this->columnIndex]; 85 | 86 | if ($this->isContinuous) { 87 | return Comparison::compare((string) $recordField, $this->numericValue, $this->operator); 88 | } 89 | 90 | return $recordField == $this->value; 91 | } 92 | 93 | /** 94 | * Returns Mean Decrease Impurity (MDI) in the node. 95 | * For terminal nodes, this value is equal to 0 96 | */ 97 | public function getNodeImpurityDecrease(int $parentRecordCount): float 98 | { 99 | if ($this->isTerminal) { 100 | return 0.0; 101 | } 102 | 103 | $nodeSampleCount = (float) count($this->records); 104 | $iT = $this->giniIndex; 105 | 106 | if ($this->leftLeaf !== null) { 107 | $pL = count($this->leftLeaf->records) / $nodeSampleCount; 108 | $iT -= $pL * $this->leftLeaf->giniIndex; 109 | } 110 | 111 | if ($this->rightLeaf !== null) { 112 | $pR = count($this->rightLeaf->records) / $nodeSampleCount; 113 | $iT -= $pR * $this->rightLeaf->giniIndex; 114 | } 115 | 116 | return $iT * $nodeSampleCount / $parentRecordCount; 117 | } 118 | 119 | /** 120 | * Returns HTML representation of the node including children nodes 121 | */ 122 | public function getHTML($columnNames = null): string 123 | { 124 | if ($this->isTerminal) { 125 | $value = "${this}->classValue"; 126 | } else { 127 | $value = $this->value; 128 | if ($columnNames !== null) { 129 | $col = $columnNames[$this->columnIndex]; 130 | } else { 131 | $col = "col_$this->columnIndex"; 132 | } 133 | 134 | if (!preg_match('/^[<>=]{1,2}/', (string) $value)) { 135 | $value = "=${value}"; 136 | } 137 | 138 | $value = "${col} ${value}
Gini: ".number_format($this->giniIndex, 2); 139 | } 140 | 141 | $str = ""; 142 | 143 | if ($this->leftLeaf !== null || $this->rightLeaf !== null) { 144 | $str .= ''; 145 | if ($this->leftLeaf !== null) { 146 | $str .= ''; 147 | } else { 148 | $str .= ''; 149 | } 150 | 151 | $str .= ''; 152 | if ($this->rightLeaf !== null) { 153 | $str .= ''; 154 | } else { 155 | $str .= ''; 156 | } 157 | 158 | $str .= ''; 159 | } 160 | 161 | $str .= '
${value}
| Yes
'.$this->leftLeaf->getHTML($columnNames).'
 No |
'.$this->rightLeaf->getHTML($columnNames).'
'; 162 | 163 | return $str; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Set.php: -------------------------------------------------------------------------------- 1 | elements = self::sanitize($elements); 23 | } 24 | 25 | /** 26 | * Creates the union of A and B. 27 | */ 28 | public static function union(self $a, self $b): self 29 | { 30 | return new self(array_merge($a->toArray(), $b->toArray())); 31 | } 32 | 33 | /** 34 | * Creates the intersection of A and B. 35 | */ 36 | public static function intersection(self $a, self $b): self 37 | { 38 | return new self(array_intersect($a->toArray(), $b->toArray())); 39 | } 40 | 41 | /** 42 | * Creates the difference of A and B. 43 | */ 44 | public static function difference(self $a, self $b): self 45 | { 46 | return new self(array_diff($a->toArray(), $b->toArray())); 47 | } 48 | 49 | /** 50 | * Creates the Cartesian product of A and B. 51 | * 52 | * @return Set[] 53 | */ 54 | public static function cartesian(self $a, self $b): array 55 | { 56 | $cartesian = []; 57 | 58 | foreach ($a as $multiplier) { 59 | foreach ($b as $multiplicand) { 60 | $cartesian[] = new self(array_merge([$multiplicand], [$multiplier])); 61 | } 62 | } 63 | 64 | return $cartesian; 65 | } 66 | 67 | /** 68 | * Creates the power set of A. 69 | * 70 | * @return Set[] 71 | */ 72 | public static function power(self $a): array 73 | { 74 | $power = [new self()]; 75 | 76 | foreach ($a as $multiplicand) { 77 | foreach ($power as $multiplier) { 78 | $power[] = new self(array_merge([$multiplicand], $multiplier->toArray())); 79 | } 80 | } 81 | 82 | return $power; 83 | } 84 | 85 | /** 86 | * @param string|int|float|bool $element 87 | */ 88 | public function add($element): self 89 | { 90 | return $this->addAll([$element]); 91 | } 92 | 93 | /** 94 | * @param string[]|int[]|float[]|bool[] $elements 95 | */ 96 | public function addAll(array $elements): self 97 | { 98 | $this->elements = self::sanitize(array_merge($this->elements, $elements)); 99 | 100 | return $this; 101 | } 102 | 103 | /** 104 | * @param string|int|float $element 105 | */ 106 | public function remove($element): self 107 | { 108 | return $this->removeAll([$element]); 109 | } 110 | 111 | /** 112 | * @param string[]|int[]|float[] $elements 113 | */ 114 | public function removeAll(array $elements): self 115 | { 116 | $this->elements = self::sanitize(array_diff($this->elements, $elements)); 117 | 118 | return $this; 119 | } 120 | 121 | /** 122 | * @param string|int|float $element 123 | */ 124 | public function contains($element): bool 125 | { 126 | return $this->containsAll([$element]); 127 | } 128 | 129 | /** 130 | * @param string[]|int[]|float[] $elements 131 | */ 132 | public function containsAll(array $elements): bool 133 | { 134 | return !array_diff($elements, $this->elements); 135 | } 136 | 137 | /** 138 | * @return string[]|int[]|float[]|bool[] 139 | */ 140 | public function toArray(): array 141 | { 142 | return $this->elements; 143 | } 144 | 145 | public function getIterator(): ArrayIterator 146 | { 147 | return new ArrayIterator($this->elements); 148 | } 149 | 150 | public function isEmpty(): bool 151 | { 152 | return $this->cardinality() == 0; 153 | } 154 | 155 | public function cardinality(): int 156 | { 157 | return count($this->elements); 158 | } 159 | 160 | /** 161 | * Removes duplicates and rewrites index. 162 | * 163 | * @param string[]|int[]|float[]|bool[] $elements 164 | * 165 | * @return string[]|int[]|float[]|bool[] 166 | */ 167 | private static function sanitize(array $elements): array 168 | { 169 | sort($elements, SORT_ASC); 170 | 171 | return array_values(array_unique($elements, SORT_ASC)); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Statistic/ANOVA.php: -------------------------------------------------------------------------------- 1 | $msbValue) { 53 | $f[$index] = $msbValue / $msw[$index]; 54 | } 55 | 56 | return $f; 57 | } 58 | 59 | private static function sumOfSquaresPerFeature(array $samples): array 60 | { 61 | $sum = array_fill(0, count($samples[0][0]), 0); 62 | foreach ($samples as $class) { 63 | foreach ($class as $sample) { 64 | foreach ($sample as $index => $feature) { 65 | $sum[$index] += $feature ** 2; 66 | } 67 | } 68 | } 69 | 70 | return $sum; 71 | } 72 | 73 | private static function sumOfFeaturesPerClass(array $samples): array 74 | { 75 | return array_map(function (array $class) { 76 | $sum = array_fill(0, count($class[0]), 0); 77 | foreach ($class as $sample) { 78 | foreach ($sample as $index => $feature) { 79 | $sum[$index] += $feature; 80 | } 81 | } 82 | 83 | return $sum; 84 | }, $samples); 85 | } 86 | 87 | private static function sumOfSquares(array $sums): array 88 | { 89 | $squares = array_fill(0, count($sums[0]), 0); 90 | foreach ($sums as $row) { 91 | foreach ($row as $index => $sum) { 92 | $squares[$index] += $sum; 93 | } 94 | } 95 | 96 | return array_map(function ($sum) { 97 | return $sum ** 2; 98 | }, $squares); 99 | } 100 | 101 | private static function squaresSum(array $sums): array 102 | { 103 | foreach ($sums as &$row) { 104 | foreach ($row as &$sum) { 105 | $sum = $sum ** 2; 106 | } 107 | } 108 | 109 | return $sums; 110 | } 111 | 112 | private static function calculateSsbn(array $samples, array $sumSamplesSquare, array $samplesPerClass, array $squareSumSamples, int $allSamples): array 113 | { 114 | $ssbn = array_fill(0, count($samples[0][0]), 0); 115 | foreach ($sumSamplesSquare as $classIndex => $class) { 116 | foreach ($class as $index => $feature) { 117 | $ssbn[$index] += $feature / $samplesPerClass[$classIndex]; 118 | } 119 | } 120 | 121 | foreach ($squareSumSamples as $index => $sum) { 122 | $ssbn[$index] -= $sum / $allSamples; 123 | } 124 | 125 | return $ssbn; 126 | } 127 | 128 | private static function calculateSswn(array $ssbn, array $ssAllSamples, array $squareSumSamples, int $allSamples): array 129 | { 130 | $sswn = []; 131 | foreach ($ssAllSamples as $index => $ss) { 132 | $sswn[$index] = ($ss - $squareSumSamples[$index] / $allSamples) - $ssbn[$index]; 133 | } 134 | 135 | return $sswn; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Statistic/Covariance.php: -------------------------------------------------------------------------------- 1 | $xi) { 37 | $yi = $y[$index]; 38 | $sum += ($xi - $meanX) * ($yi - $meanY); 39 | } 40 | 41 | if ($sample) { 42 | --$n; 43 | } 44 | 45 | return $sum / $n; 46 | } 47 | 48 | /** 49 | * Calculates covariance of two dimensions, i and k in the given data. 50 | * 51 | * @throws InvalidArgumentException 52 | * @throws \Exception 53 | */ 54 | public static function fromDataset(array $data, int $i, int $k, bool $sample = true, ?float $meanX = null, ?float $meanY = null): float 55 | { 56 | if (empty($data)) { 57 | throw InvalidArgumentException::arrayCantBeEmpty(); 58 | } 59 | 60 | $n = count($data); 61 | if ($sample && $n === 1) { 62 | throw InvalidArgumentException::arraySizeTooSmall(2); 63 | } 64 | 65 | if ($i < 0 || $k < 0 || $i >= $n || $k >= $n) { 66 | throw new InvalidArgumentException('Given indices i and k do not match with the dimensionality of data'); 67 | } 68 | 69 | if ($meanX === null || $meanY === null) { 70 | $x = array_column($data, $i); 71 | $y = array_column($data, $k); 72 | 73 | $meanX = Mean::arithmetic($x); 74 | $meanY = Mean::arithmetic($y); 75 | $sum = 0.0; 76 | foreach ($x as $index => $xi) { 77 | $yi = $y[$index]; 78 | $sum += ($xi - $meanX) * ($yi - $meanY); 79 | } 80 | } else { 81 | // In the case, whole dataset given along with dimension indices, i and k, 82 | // we would like to avoid getting column data with array_column and operate 83 | // over this extra copy of column data for memory efficiency purposes. 84 | // 85 | // Instead we traverse through the whole data and get what we actually need 86 | // without copying the data. This way, memory use will be reduced 87 | // with a slight cost of CPU utilization. 88 | $sum = 0.0; 89 | foreach ($data as $row) { 90 | $val = []; 91 | foreach ($row as $index => $col) { 92 | if ($index == $i) { 93 | $val[0] = $col - $meanX; 94 | } 95 | 96 | if ($index == $k) { 97 | $val[1] = $col - $meanY; 98 | } 99 | } 100 | 101 | $sum += $val[0] * $val[1]; 102 | } 103 | } 104 | 105 | if ($sample) { 106 | --$n; 107 | } 108 | 109 | return $sum / $n; 110 | } 111 | 112 | /** 113 | * Returns the covariance matrix of n-dimensional data 114 | * 115 | * @param array|null $means 116 | */ 117 | public static function covarianceMatrix(array $data, ?array $means = null): array 118 | { 119 | $n = count($data[0]); 120 | 121 | if ($means === null) { 122 | $means = []; 123 | for ($i = 0; $i < $n; ++$i) { 124 | $means[] = Mean::arithmetic(array_column($data, $i)); 125 | } 126 | } 127 | 128 | $cov = []; 129 | for ($i = 0; $i < $n; ++$i) { 130 | for ($k = 0; $k < $n; ++$k) { 131 | if ($i > $k) { 132 | $cov[$i][$k] = $cov[$k][$i]; 133 | } else { 134 | $cov[$i][$k] = self::fromDataset( 135 | $data, 136 | $i, 137 | $k, 138 | true, 139 | $means[$i], 140 | $means[$k] 141 | ); 142 | } 143 | } 144 | } 145 | 146 | return $cov; 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Klasifikasi/NaiveBayes.php: -------------------------------------------------------------------------------- 1 | samples = array_merge($this->samples, $samples); 65 | $this->targets = array_merge($this->targets, $targets); 66 | $this->sampleCount = count($this->samples); 67 | $this->featureCount = count($this->samples[0]); 68 | 69 | $this->labels = array_map('strval', array_flip(array_flip($this->targets))); 70 | foreach ($this->labels as $label) { 71 | $samples = $this->getSamplesByLabel($label); 72 | $this->p[$label] = count($samples) / $this->sampleCount; 73 | $this->calculateStatistics($label, $samples); 74 | } 75 | } 76 | 77 | /** 78 | * @return mixed 79 | */ 80 | protected function predictSample(array $sample) 81 | { 82 | // Use NaiveBayes assumption for each label using: 83 | // P(label|features) = P(label) * P(feature0|label) * P(feature1|label) .... P(featureN|label) 84 | // Then compare probability for each class to determine which label is most likely 85 | $predictions = []; 86 | foreach ($this->labels as $label) { 87 | $p = $this->p[$label]; 88 | for ($i = 0; $i < $this->featureCount; ++$i) { 89 | $Plf = $this->sampleProbability($sample, $i, $label); 90 | $p += $Plf; 91 | } 92 | 93 | $predictions[$label] = $p; 94 | } 95 | 96 | arsort($predictions, SORT_NUMERIC); 97 | reset($predictions); 98 | 99 | return key($predictions); 100 | } 101 | 102 | /** 103 | * Calculates vital statistics for each label & feature. Stores these 104 | * values in private array in order to avoid repeated calculation 105 | */ 106 | private function calculateStatistics(string $label, array $samples): void 107 | { 108 | $this->std[$label] = array_fill(0, $this->featureCount, 0); 109 | $this->mean[$label] = array_fill(0, $this->featureCount, 0); 110 | $this->dataType[$label] = array_fill(0, $this->featureCount, self::CONTINUOS); 111 | $this->discreteProb[$label] = array_fill(0, $this->featureCount, self::CONTINUOS); 112 | for ($i = 0; $i < $this->featureCount; ++$i) { 113 | $values = array_column($samples, $i); 114 | $numValues = count($values); 115 | // jika value non-numerik, 116 | // nominal/categorical/discrete 117 | if ($values !== array_filter($values, 'is_numeric')) { 118 | $this->dataType[$label][$i] = self::NOMINAL; 119 | $this->discreteProb[$label][$i] = array_count_values($values); 120 | $db = &$this->discreteProb[$label][$i]; 121 | $db = array_map(function ($el) use ($numValues) { 122 | return $el / $numValues; 123 | }, $db); 124 | } else { 125 | $this->mean[$label][$i] = Mean::arithmetic($values); 126 | // tambahkan epsilon, untuk menghindari hasil nol 127 | $this->std[$label][$i] = 1e-10 + StandardDeviation::population($values, false); 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * Calculates the probability P(label|sample_n) 134 | */ 135 | private function sampleProbability(array $sample, int $feature, string $label): float 136 | { 137 | $value = $sample[$feature]; 138 | if ($this->dataType[$label][$feature] == self::NOMINAL) { 139 | if (!isset($this->discreteProb[$label][$feature][$value]) || 140 | $this->discreteProb[$label][$feature][$value] == 0) { 141 | return self::EPSILON; 142 | } 143 | 144 | return $this->discreteProb[$label][$feature][$value]; 145 | } 146 | 147 | $std = $this->std[$label][$feature]; 148 | $mean = $this->mean[$label][$feature]; 149 | // Penghitungan Gaussian distribution 150 | // (Link: https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/naive_bayes.py) 151 | $pdf = -0.5 * log(2.0 * pi() * $std * $std); 152 | $pdf -= 0.5 * pow($value - $mean, 2) / ($std * $std); 153 | 154 | return $pdf; 155 | } 156 | 157 | /** 158 | * Return samples belonging to specific label 159 | */ 160 | private function getSamplesByLabel(string $label): array 161 | { 162 | $samples = []; 163 | for ($i = 0; $i < $this->sampleCount; ++$i) { 164 | if ($this->targets[$i] == $label) { 165 | $samples[] = $this->samples[$i]; 166 | } 167 | } 168 | 169 | return $samples; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /public/css/bootstrap-reboot.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md) 7 | */ 8 | *, 9 | *::before, 10 | *::after { 11 | box-sizing: border-box; 12 | } 13 | 14 | html { 15 | font-family: sans-serif; 16 | line-height: 1.15; 17 | -webkit-text-size-adjust: 100%; 18 | -ms-text-size-adjust: 100%; 19 | -ms-overflow-style: scrollbar; 20 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 21 | } 22 | 23 | @-ms-viewport { 24 | width: device-width; 25 | } 26 | 27 | article, aside, figcaption, figure, footer, header, hgroup, main, nav, section { 28 | display: block; 29 | } 30 | 31 | body { 32 | margin: 0; 33 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; 34 | font-size: 1rem; 35 | font-weight: 400; 36 | line-height: 1.5; 37 | color: #212529; 38 | text-align: left; 39 | background-color: #fff; 40 | } 41 | 42 | [tabindex="-1"]:focus { 43 | outline: 0 !important; 44 | } 45 | 46 | hr { 47 | box-sizing: content-box; 48 | height: 0; 49 | overflow: visible; 50 | } 51 | 52 | h1, h2, h3, h4, h5, h6 { 53 | margin-top: 0; 54 | margin-bottom: 0.5rem; 55 | } 56 | 57 | p { 58 | margin-top: 0; 59 | margin-bottom: 1rem; 60 | } 61 | 62 | abbr[title], 63 | abbr[data-original-title] { 64 | text-decoration: underline; 65 | -webkit-text-decoration: underline dotted; 66 | text-decoration: underline dotted; 67 | cursor: help; 68 | border-bottom: 0; 69 | } 70 | 71 | address { 72 | margin-bottom: 1rem; 73 | font-style: normal; 74 | line-height: inherit; 75 | } 76 | 77 | ol, 78 | ul, 79 | dl { 80 | margin-top: 0; 81 | margin-bottom: 1rem; 82 | } 83 | 84 | ol ol, 85 | ul ul, 86 | ol ul, 87 | ul ol { 88 | margin-bottom: 0; 89 | } 90 | 91 | dt { 92 | font-weight: 700; 93 | } 94 | 95 | dd { 96 | margin-bottom: .5rem; 97 | margin-left: 0; 98 | } 99 | 100 | blockquote { 101 | margin: 0 0 1rem; 102 | } 103 | 104 | dfn { 105 | font-style: italic; 106 | } 107 | 108 | b, 109 | strong { 110 | font-weight: bolder; 111 | } 112 | 113 | small { 114 | font-size: 80%; 115 | } 116 | 117 | sub, 118 | sup { 119 | position: relative; 120 | font-size: 75%; 121 | line-height: 0; 122 | vertical-align: baseline; 123 | } 124 | 125 | sub { 126 | bottom: -.25em; 127 | } 128 | 129 | sup { 130 | top: -.5em; 131 | } 132 | 133 | a { 134 | color: #007bff; 135 | text-decoration: none; 136 | background-color: transparent; 137 | -webkit-text-decoration-skip: objects; 138 | } 139 | 140 | a:hover { 141 | color: #0056b3; 142 | text-decoration: underline; 143 | } 144 | 145 | a:not([href]):not([tabindex]) { 146 | color: inherit; 147 | text-decoration: none; 148 | } 149 | 150 | a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus { 151 | color: inherit; 152 | text-decoration: none; 153 | } 154 | 155 | a:not([href]):not([tabindex]):focus { 156 | outline: 0; 157 | } 158 | 159 | pre, 160 | code, 161 | kbd, 162 | samp { 163 | font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; 164 | font-size: 1em; 165 | } 166 | 167 | pre { 168 | margin-top: 0; 169 | margin-bottom: 1rem; 170 | overflow: auto; 171 | -ms-overflow-style: scrollbar; 172 | } 173 | 174 | figure { 175 | margin: 0 0 1rem; 176 | } 177 | 178 | img { 179 | vertical-align: middle; 180 | border-style: none; 181 | } 182 | 183 | svg { 184 | overflow: hidden; 185 | vertical-align: middle; 186 | } 187 | 188 | table { 189 | border-collapse: collapse; 190 | } 191 | 192 | caption { 193 | padding-top: 0.75rem; 194 | padding-bottom: 0.75rem; 195 | color: #6c757d; 196 | text-align: left; 197 | caption-side: bottom; 198 | } 199 | 200 | th { 201 | text-align: inherit; 202 | } 203 | 204 | label { 205 | display: inline-block; 206 | margin-bottom: 0.5rem; 207 | } 208 | 209 | button { 210 | border-radius: 0; 211 | } 212 | 213 | button:focus { 214 | outline: 1px dotted; 215 | outline: 5px auto -webkit-focus-ring-color; 216 | } 217 | 218 | input, 219 | button, 220 | select, 221 | optgroup, 222 | textarea { 223 | margin: 0; 224 | font-family: inherit; 225 | font-size: inherit; 226 | line-height: inherit; 227 | } 228 | 229 | button, 230 | input { 231 | overflow: visible; 232 | } 233 | 234 | button, 235 | select { 236 | text-transform: none; 237 | } 238 | 239 | button, 240 | html [type="button"], 241 | [type="reset"], 242 | [type="submit"] { 243 | -webkit-appearance: button; 244 | } 245 | 246 | button::-moz-focus-inner, 247 | [type="button"]::-moz-focus-inner, 248 | [type="reset"]::-moz-focus-inner, 249 | [type="submit"]::-moz-focus-inner { 250 | padding: 0; 251 | border-style: none; 252 | } 253 | 254 | input[type="radio"], 255 | input[type="checkbox"] { 256 | box-sizing: border-box; 257 | padding: 0; 258 | } 259 | 260 | input[type="date"], 261 | input[type="time"], 262 | input[type="datetime-local"], 263 | input[type="month"] { 264 | -webkit-appearance: listbox; 265 | } 266 | 267 | textarea { 268 | overflow: auto; 269 | resize: vertical; 270 | } 271 | 272 | fieldset { 273 | min-width: 0; 274 | padding: 0; 275 | margin: 0; 276 | border: 0; 277 | } 278 | 279 | legend { 280 | display: block; 281 | width: 100%; 282 | max-width: 100%; 283 | padding: 0; 284 | margin-bottom: .5rem; 285 | font-size: 1.5rem; 286 | line-height: inherit; 287 | color: inherit; 288 | white-space: normal; 289 | } 290 | 291 | progress { 292 | vertical-align: baseline; 293 | } 294 | 295 | [type="number"]::-webkit-inner-spin-button, 296 | [type="number"]::-webkit-outer-spin-button { 297 | height: auto; 298 | } 299 | 300 | [type="search"] { 301 | outline-offset: -2px; 302 | -webkit-appearance: none; 303 | } 304 | 305 | [type="search"]::-webkit-search-cancel-button, 306 | [type="search"]::-webkit-search-decoration { 307 | -webkit-appearance: none; 308 | } 309 | 310 | ::-webkit-file-upload-button { 311 | font: inherit; 312 | -webkit-appearance: button; 313 | } 314 | 315 | output { 316 | display: inline-block; 317 | } 318 | 319 | summary { 320 | display: list-item; 321 | cursor: pointer; 322 | } 323 | 324 | template { 325 | display: none; 326 | } 327 | 328 | [hidden] { 329 | display: none !important; 330 | } 331 | /*# sourceMappingURL=bootstrap-reboot.css.map */ -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/Matrix.php: -------------------------------------------------------------------------------- 1 | rows = 1; 41 | $this->columns = count($matrix); 42 | $matrix = [$matrix]; 43 | } else { 44 | $this->rows = count($matrix); 45 | $this->columns = count($matrix[0]); 46 | } 47 | 48 | if ($validate) { 49 | for ($i = 0; $i < $this->rows; ++$i) { 50 | if (count($matrix[$i]) !== $this->columns) { 51 | throw InvalidArgumentException::matrixDimensionsDidNotMatch(); 52 | } 53 | } 54 | } 55 | 56 | $this->matrix = $matrix; 57 | } 58 | 59 | public static function fromFlatArray(array $array): self 60 | { 61 | $matrix = []; 62 | foreach ($array as $value) { 63 | $matrix[] = [$value]; 64 | } 65 | 66 | return new self($matrix); 67 | } 68 | 69 | public function toArray(): array 70 | { 71 | return $this->matrix; 72 | } 73 | 74 | public function toScalar(): float 75 | { 76 | return $this->matrix[0][0]; 77 | } 78 | 79 | public function getRows(): int 80 | { 81 | return $this->rows; 82 | } 83 | 84 | public function getColumns(): int 85 | { 86 | return $this->columns; 87 | } 88 | 89 | /** 90 | * @throws MatrixException 91 | */ 92 | public function getColumnValues($column): array 93 | { 94 | if ($column >= $this->columns) { 95 | throw MatrixException::columnOutOfRange(); 96 | } 97 | 98 | return array_column($this->matrix, $column); 99 | } 100 | 101 | /** 102 | * @return float|int 103 | * 104 | * @throws MatrixException 105 | */ 106 | public function getDeterminant() 107 | { 108 | if ($this->determinant !== null) { 109 | return $this->determinant; 110 | } 111 | 112 | if (!$this->isSquare()) { 113 | throw MatrixException::notSquareMatrix(); 114 | } 115 | 116 | $lu = new LUDecomposition($this); 117 | 118 | return $this->determinant = $lu->det(); 119 | } 120 | 121 | public function isSquare(): bool 122 | { 123 | return $this->columns === $this->rows; 124 | } 125 | 126 | public function transpose(): self 127 | { 128 | if ($this->rows == 1) { 129 | $matrix = array_map(function ($el) { 130 | return [$el]; 131 | }, $this->matrix[0]); 132 | } else { 133 | $matrix = array_map(null, ...$this->matrix); 134 | } 135 | 136 | return new self($matrix, false); 137 | } 138 | 139 | public function multiply(self $matrix): self 140 | { 141 | if ($this->columns != $matrix->getRows()) { 142 | throw InvalidArgumentException::inconsistentMatrixSupplied(); 143 | } 144 | 145 | $product = []; 146 | $multiplier = $matrix->toArray(); 147 | for ($i = 0; $i < $this->rows; ++$i) { 148 | $columns = $matrix->getColumns(); 149 | for ($j = 0; $j < $columns; ++$j) { 150 | $product[$i][$j] = 0; 151 | for ($k = 0; $k < $this->columns; ++$k) { 152 | $product[$i][$j] += $this->matrix[$i][$k] * $multiplier[$k][$j]; 153 | } 154 | } 155 | } 156 | 157 | return new self($product, false); 158 | } 159 | 160 | public function divideByScalar($value): self 161 | { 162 | $newMatrix = []; 163 | for ($i = 0; $i < $this->rows; ++$i) { 164 | for ($j = 0; $j < $this->columns; ++$j) { 165 | $newMatrix[$i][$j] = $this->matrix[$i][$j] / $value; 166 | } 167 | } 168 | 169 | return new self($newMatrix, false); 170 | } 171 | 172 | public function multiplyByScalar($value): self 173 | { 174 | $newMatrix = []; 175 | for ($i = 0; $i < $this->rows; ++$i) { 176 | for ($j = 0; $j < $this->columns; ++$j) { 177 | $newMatrix[$i][$j] = $this->matrix[$i][$j] * $value; 178 | } 179 | } 180 | 181 | return new self($newMatrix, false); 182 | } 183 | 184 | /** 185 | * Element-wise addition of the matrix with another one 186 | */ 187 | public function add(self $other): self 188 | { 189 | return $this->_add($other); 190 | } 191 | 192 | /** 193 | * Element-wise subtracting of another matrix from this one 194 | */ 195 | public function subtract(self $other): self 196 | { 197 | return $this->_add($other, -1); 198 | } 199 | 200 | public function inverse(): self 201 | { 202 | if (!$this->isSquare()) { 203 | throw MatrixException::notSquareMatrix(); 204 | } 205 | 206 | $LU = new LUDecomposition($this); 207 | $identity = $this->getIdentity(); 208 | $inverse = $LU->solve($identity); 209 | 210 | return new self($inverse, false); 211 | } 212 | 213 | public function crossOut(int $row, int $column): self 214 | { 215 | $newMatrix = []; 216 | $r = 0; 217 | for ($i = 0; $i < $this->rows; ++$i) { 218 | $c = 0; 219 | if ($row != $i) { 220 | for ($j = 0; $j < $this->columns; ++$j) { 221 | if ($column != $j) { 222 | $newMatrix[$r][$c] = $this->matrix[$i][$j]; 223 | ++$c; 224 | } 225 | } 226 | 227 | ++$r; 228 | } 229 | } 230 | 231 | return new self($newMatrix, false); 232 | } 233 | 234 | public function isSingular(): bool 235 | { 236 | return $this->getDeterminant() == 0; 237 | } 238 | 239 | /** 240 | * Frobenius norm (Hilbert–Schmidt norm, Euclidean norm) (‖A‖F) 241 | * Square root of the sum of the square of all elements. 242 | * 243 | * https://en.wikipedia.org/wiki/Matrix_norm#Frobenius_norm 244 | * 245 | * _____________ 246 | * /ᵐ ⁿ 247 | * ‖A‖F = √ Σ Σ |aᵢⱼ|² 248 | * ᵢ₌₁ ᵢ₌₁ 249 | */ 250 | public function frobeniusNorm(): float 251 | { 252 | $squareSum = 0; 253 | for ($i = 0; $i < $this->rows; ++$i) { 254 | for ($j = 0; $j < $this->columns; ++$j) { 255 | $squareSum += ($this->matrix[$i][$j]) ** 2; 256 | } 257 | } 258 | 259 | return sqrt($squareSum); 260 | } 261 | 262 | /** 263 | * Returns the transpose of given array 264 | */ 265 | public static function transposeArray(array $array): array 266 | { 267 | return (new self($array, false))->transpose()->toArray(); 268 | } 269 | 270 | /** 271 | * Returns the dot product of two arrays
272 | * Matrix::dot(x, y) ==> x.y' 273 | */ 274 | public static function dot(array $array1, array $array2): array 275 | { 276 | $m1 = new self($array1, false); 277 | $m2 = new self($array2, false); 278 | 279 | return $m1->multiply($m2->transpose())->toArray()[0]; 280 | } 281 | 282 | /** 283 | * Element-wise addition or substraction depending on the given sign parameter 284 | */ 285 | private function _add(self $other, int $sign = 1): self 286 | { 287 | $a1 = $this->toArray(); 288 | $a2 = $other->toArray(); 289 | 290 | $newMatrix = []; 291 | for ($i = 0; $i < $this->rows; ++$i) { 292 | for ($k = 0; $k < $this->columns; ++$k) { 293 | $newMatrix[$i][$k] = $a1[$i][$k] + $sign * $a2[$i][$k]; 294 | } 295 | } 296 | 297 | return new self($newMatrix, false); 298 | } 299 | 300 | /** 301 | * Returns diagonal identity matrix of the same size of this matrix 302 | */ 303 | private function getIdentity(): self 304 | { 305 | $array = array_fill(0, $this->rows, array_fill(0, $this->columns, 0)); 306 | for ($i = 0; $i < $this->rows; ++$i) { 307 | $array[$i][$i] = 1; 308 | } 309 | 310 | return new self($array, false); 311 | } 312 | } 313 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Math/LinearAlgebra/LUDecomposition.php: -------------------------------------------------------------------------------- 1 | = n, the LU decomposition is an m-by-n 9 | * unit lower triangular matrix L, an n-by-n upper triangular matrix U, 10 | * and a permutation vector piv of length m so that A(piv,:) = L*U. 11 | * If m < n, then L is m-by-m and U is m-by-n. 12 | * 13 | * The LU decompostion with pivoting always exists, even if the matrix is 14 | * singular, so the constructor will never fail. The primary use of the 15 | * LU decomposition is in the solution of square systems of simultaneous 16 | * linear equations. This will fail if isNonsingular() returns false. 17 | * 18 | * @author Paul Meagher 19 | * @author Bartosz Matosiuk 20 | * @author Michael Bommarito 21 | * 22 | * @version 1.1 23 | * 24 | * @license PHP v3.0 25 | * 26 | * Slightly changed to adapt the original code to PHP-ML library 27 | * @date 2017/04/24 28 | * 29 | * @author Mustafa Karabulut 30 | */ 31 | 32 | namespace Phpml\Math\LinearAlgebra; 33 | 34 | use Phpml\Exception\MatrixException; 35 | use Phpml\Math\Matrix; 36 | 37 | class LUDecomposition 38 | { 39 | /** 40 | * Decomposition storage 41 | * 42 | * @var array 43 | */ 44 | private $LU = []; 45 | 46 | /** 47 | * Row dimension. 48 | * 49 | * @var int 50 | */ 51 | private $m; 52 | 53 | /** 54 | * Column dimension. 55 | * 56 | * @var int 57 | */ 58 | private $n; 59 | 60 | /** 61 | * Pivot sign. 62 | * 63 | * @var int 64 | */ 65 | private $pivsign; 66 | 67 | /** 68 | * Internal storage of pivot vector. 69 | * 70 | * @var array 71 | */ 72 | private $piv = []; 73 | 74 | /** 75 | * Constructs Structure to access L, U and piv. 76 | * 77 | * @param Matrix $A Rectangular matrix 78 | * 79 | * @throws MatrixException 80 | */ 81 | public function __construct(Matrix $A) 82 | { 83 | if ($A->getRows() != $A->getColumns()) { 84 | throw MatrixException::notSquareMatrix(); 85 | } 86 | 87 | // Use a "left-looking", dot-product, Crout/Doolittle algorithm. 88 | $this->LU = $A->toArray(); 89 | $this->m = $A->getRows(); 90 | $this->n = $A->getColumns(); 91 | for ($i = 0; $i < $this->m; ++$i) { 92 | $this->piv[$i] = $i; 93 | } 94 | 95 | $this->pivsign = 1; 96 | $LUcolj = []; 97 | 98 | // Outer loop. 99 | for ($j = 0; $j < $this->n; ++$j) { 100 | // Make a copy of the j-th column to localize references. 101 | for ($i = 0; $i < $this->m; ++$i) { 102 | $LUcolj[$i] = &$this->LU[$i][$j]; 103 | } 104 | 105 | // Apply previous transformations. 106 | for ($i = 0; $i < $this->m; ++$i) { 107 | $LUrowi = $this->LU[$i]; 108 | // Most of the time is spent in the following dot product. 109 | $kmax = min($i, $j); 110 | $s = 0.0; 111 | for ($k = 0; $k < $kmax; ++$k) { 112 | $s += $LUrowi[$k] * $LUcolj[$k]; 113 | } 114 | 115 | $LUrowi[$j] = $LUcolj[$i] -= $s; 116 | } 117 | 118 | // Find pivot and exchange if necessary. 119 | $p = $j; 120 | for ($i = $j + 1; $i < $this->m; ++$i) { 121 | if (abs($LUcolj[$i]) > abs($LUcolj[$p])) { 122 | $p = $i; 123 | } 124 | } 125 | 126 | if ($p != $j) { 127 | for ($k = 0; $k < $this->n; ++$k) { 128 | $t = $this->LU[$p][$k]; 129 | $this->LU[$p][$k] = $this->LU[$j][$k]; 130 | $this->LU[$j][$k] = $t; 131 | } 132 | 133 | $k = $this->piv[$p]; 134 | $this->piv[$p] = $this->piv[$j]; 135 | $this->piv[$j] = $k; 136 | $this->pivsign = $this->pivsign * -1; 137 | } 138 | 139 | // Compute multipliers. 140 | if (($j < $this->m) && ($this->LU[$j][$j] != 0.0)) { 141 | for ($i = $j + 1; $i < $this->m; ++$i) { 142 | $this->LU[$i][$j] /= $this->LU[$j][$j]; 143 | } 144 | } 145 | } 146 | } 147 | 148 | /** 149 | * Get lower triangular factor. 150 | * 151 | * @return Matrix Lower triangular factor 152 | */ 153 | public function getL(): Matrix 154 | { 155 | $L = []; 156 | for ($i = 0; $i < $this->m; ++$i) { 157 | for ($j = 0; $j < $this->n; ++$j) { 158 | if ($i > $j) { 159 | $L[$i][$j] = $this->LU[$i][$j]; 160 | } elseif ($i == $j) { 161 | $L[$i][$j] = 1.0; 162 | } else { 163 | $L[$i][$j] = 0.0; 164 | } 165 | } 166 | } 167 | 168 | return new Matrix($L); 169 | } 170 | 171 | /** 172 | * Get upper triangular factor. 173 | * 174 | * @return Matrix Upper triangular factor 175 | */ 176 | public function getU(): Matrix 177 | { 178 | $U = []; 179 | for ($i = 0; $i < $this->n; ++$i) { 180 | for ($j = 0; $j < $this->n; ++$j) { 181 | if ($i <= $j) { 182 | $U[$i][$j] = $this->LU[$i][$j]; 183 | } else { 184 | $U[$i][$j] = 0.0; 185 | } 186 | } 187 | } 188 | 189 | return new Matrix($U); 190 | } 191 | 192 | /** 193 | * Return pivot permutation vector. 194 | * 195 | * @return array Pivot vector 196 | */ 197 | public function getPivot(): array 198 | { 199 | return $this->piv; 200 | } 201 | 202 | /** 203 | * Alias for getPivot 204 | * 205 | * @see getPivot 206 | */ 207 | public function getDoublePivot() 208 | { 209 | return $this->getPivot(); 210 | } 211 | 212 | /** 213 | * Is the matrix nonsingular? 214 | * 215 | * @return bool true if U, and hence A, is nonsingular. 216 | */ 217 | public function isNonsingular(): bool 218 | { 219 | for ($j = 0; $j < $this->n; ++$j) { 220 | if ($this->LU[$j][$j] == 0) { 221 | return false; 222 | } 223 | } 224 | 225 | return true; 226 | } 227 | 228 | /** 229 | * Count determinants 230 | * 231 | * @return float|int d matrix determinant 232 | * 233 | * @throws MatrixException 234 | */ 235 | public function det() 236 | { 237 | if ($this->m !== $this->n) { 238 | throw MatrixException::notSquareMatrix(); 239 | } 240 | 241 | $d = $this->pivsign; 242 | for ($j = 0; $j < $this->n; ++$j) { 243 | $d *= $this->LU[$j][$j]; 244 | } 245 | 246 | return $d; 247 | } 248 | 249 | /** 250 | * Solve A*X = B 251 | * 252 | * @param Matrix $B A Matrix with as many rows as A and any number of columns. 253 | * 254 | * @return array X so that L*U*X = B(piv,:) 255 | * 256 | * @throws MatrixException 257 | */ 258 | public function solve(Matrix $B): array 259 | { 260 | if ($B->getRows() != $this->m) { 261 | throw MatrixException::notSquareMatrix(); 262 | } 263 | 264 | if (!$this->isNonsingular()) { 265 | throw MatrixException::singularMatrix(); 266 | } 267 | 268 | // Copy right hand side with pivoting 269 | $nx = $B->getColumns(); 270 | $X = $this->getSubMatrix($B->toArray(), $this->piv, 0, $nx - 1); 271 | // Solve L*Y = B(piv,:) 272 | for ($k = 0; $k < $this->n; ++$k) { 273 | for ($i = $k + 1; $i < $this->n; ++$i) { 274 | for ($j = 0; $j < $nx; ++$j) { 275 | $X[$i][$j] -= $X[$k][$j] * $this->LU[$i][$k]; 276 | } 277 | } 278 | } 279 | 280 | // Solve U*X = Y; 281 | for ($k = $this->n - 1; $k >= 0; --$k) { 282 | for ($j = 0; $j < $nx; ++$j) { 283 | $X[$k][$j] /= $this->LU[$k][$k]; 284 | } 285 | 286 | for ($i = 0; $i < $k; ++$i) { 287 | for ($j = 0; $j < $nx; ++$j) { 288 | $X[$i][$j] -= $X[$k][$j] * $this->LU[$i][$k]; 289 | } 290 | } 291 | } 292 | 293 | return $X; 294 | } 295 | 296 | protected function getSubMatrix(array $matrix, array $RL, int $j0, int $jF): array 297 | { 298 | $m = count($RL); 299 | $n = $jF - $j0; 300 | $R = array_fill(0, $m, array_fill(0, $n + 1, 0.0)); 301 | 302 | for ($i = 0; $i < $m; ++$i) { 303 | for ($j = $j0; $j <= $jF; ++$j) { 304 | $R[$i][$j - $j0] = $matrix[$RL[$i]][$j]; 305 | } 306 | } 307 | 308 | return $R; 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /vendor/composer/ClassLoader.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see http://www.php-fig.org/psr/psr-0/ 41 | * @see http://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | // PSR-4 46 | private $prefixLengthsPsr4 = array(); 47 | private $prefixDirsPsr4 = array(); 48 | private $fallbackDirsPsr4 = array(); 49 | 50 | // PSR-0 51 | private $prefixesPsr0 = array(); 52 | private $fallbackDirsPsr0 = array(); 53 | 54 | private $useIncludePath = false; 55 | private $classMap = array(); 56 | private $classMapAuthoritative = false; 57 | private $missingClasses = array(); 58 | private $apcuPrefix; 59 | 60 | public function getPrefixes() 61 | { 62 | if (!empty($this->prefixesPsr0)) { 63 | return call_user_func_array('array_merge', $this->prefixesPsr0); 64 | } 65 | 66 | return array(); 67 | } 68 | 69 | public function getPrefixesPsr4() 70 | { 71 | return $this->prefixDirsPsr4; 72 | } 73 | 74 | public function getFallbackDirs() 75 | { 76 | return $this->fallbackDirsPsr0; 77 | } 78 | 79 | public function getFallbackDirsPsr4() 80 | { 81 | return $this->fallbackDirsPsr4; 82 | } 83 | 84 | public function getClassMap() 85 | { 86 | return $this->classMap; 87 | } 88 | 89 | /** 90 | * @param array $classMap Class to filename map 91 | */ 92 | public function addClassMap(array $classMap) 93 | { 94 | if ($this->classMap) { 95 | $this->classMap = array_merge($this->classMap, $classMap); 96 | } else { 97 | $this->classMap = $classMap; 98 | } 99 | } 100 | 101 | /** 102 | * Registers a set of PSR-0 directories for a given prefix, either 103 | * appending or prepending to the ones previously set for this prefix. 104 | * 105 | * @param string $prefix The prefix 106 | * @param array|string $paths The PSR-0 root directories 107 | * @param bool $prepend Whether to prepend the directories 108 | */ 109 | public function add($prefix, $paths, $prepend = false) 110 | { 111 | if (!$prefix) { 112 | if ($prepend) { 113 | $this->fallbackDirsPsr0 = array_merge( 114 | (array) $paths, 115 | $this->fallbackDirsPsr0 116 | ); 117 | } else { 118 | $this->fallbackDirsPsr0 = array_merge( 119 | $this->fallbackDirsPsr0, 120 | (array) $paths 121 | ); 122 | } 123 | 124 | return; 125 | } 126 | 127 | $first = $prefix[0]; 128 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 129 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 130 | 131 | return; 132 | } 133 | if ($prepend) { 134 | $this->prefixesPsr0[$first][$prefix] = array_merge( 135 | (array) $paths, 136 | $this->prefixesPsr0[$first][$prefix] 137 | ); 138 | } else { 139 | $this->prefixesPsr0[$first][$prefix] = array_merge( 140 | $this->prefixesPsr0[$first][$prefix], 141 | (array) $paths 142 | ); 143 | } 144 | } 145 | 146 | /** 147 | * Registers a set of PSR-4 directories for a given namespace, either 148 | * appending or prepending to the ones previously set for this namespace. 149 | * 150 | * @param string $prefix The prefix/namespace, with trailing '\\' 151 | * @param array|string $paths The PSR-4 base directories 152 | * @param bool $prepend Whether to prepend the directories 153 | * 154 | * @throws \InvalidArgumentException 155 | */ 156 | public function addPsr4($prefix, $paths, $prepend = false) 157 | { 158 | if (!$prefix) { 159 | // Register directories for the root namespace. 160 | if ($prepend) { 161 | $this->fallbackDirsPsr4 = array_merge( 162 | (array) $paths, 163 | $this->fallbackDirsPsr4 164 | ); 165 | } else { 166 | $this->fallbackDirsPsr4 = array_merge( 167 | $this->fallbackDirsPsr4, 168 | (array) $paths 169 | ); 170 | } 171 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 172 | // Register directories for a new namespace. 173 | $length = strlen($prefix); 174 | if ('\\' !== $prefix[$length - 1]) { 175 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 176 | } 177 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 178 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 179 | } elseif ($prepend) { 180 | // Prepend directories for an already registered namespace. 181 | $this->prefixDirsPsr4[$prefix] = array_merge( 182 | (array) $paths, 183 | $this->prefixDirsPsr4[$prefix] 184 | ); 185 | } else { 186 | // Append directories for an already registered namespace. 187 | $this->prefixDirsPsr4[$prefix] = array_merge( 188 | $this->prefixDirsPsr4[$prefix], 189 | (array) $paths 190 | ); 191 | } 192 | } 193 | 194 | /** 195 | * Registers a set of PSR-0 directories for a given prefix, 196 | * replacing any others previously set for this prefix. 197 | * 198 | * @param string $prefix The prefix 199 | * @param array|string $paths The PSR-0 base directories 200 | */ 201 | public function set($prefix, $paths) 202 | { 203 | if (!$prefix) { 204 | $this->fallbackDirsPsr0 = (array) $paths; 205 | } else { 206 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 207 | } 208 | } 209 | 210 | /** 211 | * Registers a set of PSR-4 directories for a given namespace, 212 | * replacing any others previously set for this namespace. 213 | * 214 | * @param string $prefix The prefix/namespace, with trailing '\\' 215 | * @param array|string $paths The PSR-4 base directories 216 | * 217 | * @throws \InvalidArgumentException 218 | */ 219 | public function setPsr4($prefix, $paths) 220 | { 221 | if (!$prefix) { 222 | $this->fallbackDirsPsr4 = (array) $paths; 223 | } else { 224 | $length = strlen($prefix); 225 | if ('\\' !== $prefix[$length - 1]) { 226 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 227 | } 228 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 229 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 230 | } 231 | } 232 | 233 | /** 234 | * Turns on searching the include path for class files. 235 | * 236 | * @param bool $useIncludePath 237 | */ 238 | public function setUseIncludePath($useIncludePath) 239 | { 240 | $this->useIncludePath = $useIncludePath; 241 | } 242 | 243 | /** 244 | * Can be used to check if the autoloader uses the include path to check 245 | * for classes. 246 | * 247 | * @return bool 248 | */ 249 | public function getUseIncludePath() 250 | { 251 | return $this->useIncludePath; 252 | } 253 | 254 | /** 255 | * Turns off searching the prefix and fallback directories for classes 256 | * that have not been registered with the class map. 257 | * 258 | * @param bool $classMapAuthoritative 259 | */ 260 | public function setClassMapAuthoritative($classMapAuthoritative) 261 | { 262 | $this->classMapAuthoritative = $classMapAuthoritative; 263 | } 264 | 265 | /** 266 | * Should class lookup fail if not found in the current class map? 267 | * 268 | * @return bool 269 | */ 270 | public function isClassMapAuthoritative() 271 | { 272 | return $this->classMapAuthoritative; 273 | } 274 | 275 | /** 276 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 277 | * 278 | * @param string|null $apcuPrefix 279 | */ 280 | public function setApcuPrefix($apcuPrefix) 281 | { 282 | $this->apcuPrefix = function_exists('apcu_fetch') && ini_get('apc.enabled') ? $apcuPrefix : null; 283 | } 284 | 285 | /** 286 | * The APCu prefix in use, or null if APCu caching is not enabled. 287 | * 288 | * @return string|null 289 | */ 290 | public function getApcuPrefix() 291 | { 292 | return $this->apcuPrefix; 293 | } 294 | 295 | /** 296 | * Registers this instance as an autoloader. 297 | * 298 | * @param bool $prepend Whether to prepend the autoloader or not 299 | */ 300 | public function register($prepend = false) 301 | { 302 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 303 | } 304 | 305 | /** 306 | * Unregisters this instance as an autoloader. 307 | */ 308 | public function unregister() 309 | { 310 | spl_autoload_unregister(array($this, 'loadClass')); 311 | } 312 | 313 | /** 314 | * Loads the given class or interface. 315 | * 316 | * @param string $class The name of the class 317 | * @return bool|null True if loaded, null otherwise 318 | */ 319 | public function loadClass($class) 320 | { 321 | if ($file = $this->findFile($class)) { 322 | includeFile($file); 323 | 324 | return true; 325 | } 326 | } 327 | 328 | /** 329 | * Finds the path to the file where the class is defined. 330 | * 331 | * @param string $class The name of the class 332 | * 333 | * @return string|false The path if found, false otherwise 334 | */ 335 | public function findFile($class) 336 | { 337 | // class map lookup 338 | if (isset($this->classMap[$class])) { 339 | return $this->classMap[$class]; 340 | } 341 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 342 | return false; 343 | } 344 | if (null !== $this->apcuPrefix) { 345 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 346 | if ($hit) { 347 | return $file; 348 | } 349 | } 350 | 351 | $file = $this->findFileWithExtension($class, '.php'); 352 | 353 | // Search for Hack files if we are running on HHVM 354 | if (false === $file && defined('HHVM_VERSION')) { 355 | $file = $this->findFileWithExtension($class, '.hh'); 356 | } 357 | 358 | if (null !== $this->apcuPrefix) { 359 | apcu_add($this->apcuPrefix.$class, $file); 360 | } 361 | 362 | if (false === $file) { 363 | // Remember that this class does not exist. 364 | $this->missingClasses[$class] = true; 365 | } 366 | 367 | return $file; 368 | } 369 | 370 | private function findFileWithExtension($class, $ext) 371 | { 372 | // PSR-4 lookup 373 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 374 | 375 | $first = $class[0]; 376 | if (isset($this->prefixLengthsPsr4[$first])) { 377 | $subPath = $class; 378 | while (false !== $lastPos = strrpos($subPath, '\\')) { 379 | $subPath = substr($subPath, 0, $lastPos); 380 | $search = $subPath . '\\'; 381 | if (isset($this->prefixDirsPsr4[$search])) { 382 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 383 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 384 | if (file_exists($file = $dir . $pathEnd)) { 385 | return $file; 386 | } 387 | } 388 | } 389 | } 390 | } 391 | 392 | // PSR-4 fallback dirs 393 | foreach ($this->fallbackDirsPsr4 as $dir) { 394 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 395 | return $file; 396 | } 397 | } 398 | 399 | // PSR-0 lookup 400 | if (false !== $pos = strrpos($class, '\\')) { 401 | // namespaced class name 402 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 403 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 404 | } else { 405 | // PEAR-like class name 406 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 407 | } 408 | 409 | if (isset($this->prefixesPsr0[$first])) { 410 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 411 | if (0 === strpos($class, $prefix)) { 412 | foreach ($dirs as $dir) { 413 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 414 | return $file; 415 | } 416 | } 417 | } 418 | } 419 | } 420 | 421 | // PSR-0 fallback dirs 422 | foreach ($this->fallbackDirsPsr0 as $dir) { 423 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 424 | return $file; 425 | } 426 | } 427 | 428 | // PSR-0 include paths. 429 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 430 | return $file; 431 | } 432 | 433 | return false; 434 | } 435 | } 436 | 437 | /** 438 | * Scope isolated include. 439 | * 440 | * Prevents access to $this/self from included files. 441 | */ 442 | function includeFile($file) 443 | { 444 | include $file; 445 | } 446 | -------------------------------------------------------------------------------- /joko-dm/Datamining/Klasifikasi/DecisionTree.php: -------------------------------------------------------------------------------- 1 | maxDepth = $maxDepth; 74 | } 75 | 76 | public function train(array $samples, array $targets): void 77 | { 78 | $this->samples = array_merge($this->samples, $samples); 79 | $this->targets = array_merge($this->targets, $targets); 80 | 81 | $this->featureCount = count($this->samples[0]); 82 | $this->columnTypes = self::getColumnTypes($this->samples); 83 | $this->labels = array_keys(array_count_values($this->targets)); 84 | $this->tree = $this->getSplitLeaf(range(0, count($this->samples) - 1)); 85 | 86 | // Each time the tree is trained, feature importances are reset so that 87 | // we will have to compute it again depending on the new data 88 | $this->featureImportances = null; 89 | 90 | // If column names are given or computed before, then there is no 91 | // need to init it and accidentally remove the previous given names 92 | if ($this->columnNames === []) { 93 | $this->columnNames = range(0, $this->featureCount - 1); 94 | } elseif (count($this->columnNames) > $this->featureCount) { 95 | $this->columnNames = array_slice($this->columnNames, 0, $this->featureCount); 96 | } elseif (count($this->columnNames) < $this->featureCount) { 97 | $this->columnNames = array_merge( 98 | $this->columnNames, 99 | range(count($this->columnNames), $this->featureCount - 1) 100 | ); 101 | } 102 | } 103 | 104 | public static function getColumnTypes(array $samples): array 105 | { 106 | $types = []; 107 | $featureCount = count($samples[0]); 108 | for ($i = 0; $i < $featureCount; ++$i) { 109 | $values = array_column($samples, $i); 110 | $isCategorical = self::isCategoricalColumn($values); 111 | $types[] = $isCategorical ? self::NOMINAL : self::CONTINUOUS; 112 | } 113 | 114 | return $types; 115 | } 116 | 117 | 118 | //Penghitungan Index Gini dan Gini Gain 119 | public function getGiniIndex($baseValue, array $colValues, array $targets): float 120 | { 121 | $countMatrix = []; 122 | foreach ($this->labels as $label) { 123 | $countMatrix[$label] = [0, 0]; 124 | } 125 | 126 | foreach ($colValues as $index => $value) { 127 | $label = $targets[$index]; 128 | $rowIndex = $value === $baseValue ? 0 : 1; 129 | ++$countMatrix[$label][$rowIndex]; 130 | } 131 | 132 | $giniParts = [0, 0]; 133 | for ($i = 0; $i <= 1; ++$i) { 134 | $part = 0; 135 | $sum = array_sum(array_column($countMatrix, $i)); 136 | if ($sum > 0) { 137 | foreach ($this->labels as $label) { 138 | $part += pow($countMatrix[$label][$i] / (float) $sum, 2); 139 | } 140 | } 141 | 142 | $giniParts[$i] = (1 - $part) * $sum; 143 | } 144 | 145 | return array_sum($giniParts) / count($colValues); 146 | } 147 | 148 | /** 149 | * This method is used to set number of columns to be used 150 | * when deciding a split at an internal node of the tree.
151 | * If the value is given 0, then all features are used (default behaviour), 152 | * otherwise the given value will be used as a maximum for number of columns 153 | * randomly selected for each split operation. 154 | * 155 | * @return $this 156 | * 157 | * @throws InvalidArgumentException 158 | */ 159 | public function setNumFeatures(int $numFeatures) 160 | { 161 | if ($numFeatures < 0) { 162 | throw new InvalidArgumentException('Selected column count should be greater or equal to zero'); 163 | } 164 | 165 | $this->numUsableFeatures = $numFeatures; 166 | 167 | return $this; 168 | } 169 | 170 | /** 171 | * A string array to represent columns. Useful when HTML output or 172 | * column importances are desired to be inspected. 173 | * 174 | * @return $this 175 | * 176 | * @throws InvalidArgumentException 177 | */ 178 | public function setColumnNames(array $names) 179 | { 180 | if ($this->featureCount !== 0 && count($names) !== $this->featureCount) { 181 | throw new InvalidArgumentException(sprintf('Length of the given array should be equal to feature count %s', $this->featureCount)); 182 | } 183 | 184 | $this->columnNames = $names; 185 | 186 | return $this; 187 | } 188 | 189 | public function getHtml(): string 190 | { 191 | return $this->tree->getHTML($this->columnNames); 192 | } 193 | 194 | /** 195 | * This will return an array including an importance value for 196 | * each column in the given dataset. The importance values are 197 | * normalized and their total makes 1.
198 | */ 199 | public function getFeatureImportances(): array 200 | { 201 | if ($this->featureImportances !== null) { 202 | return $this->featureImportances; 203 | } 204 | 205 | $sampleCount = count($this->samples); 206 | $this->featureImportances = []; 207 | foreach ($this->columnNames as $column => $columnName) { 208 | $nodes = $this->getSplitNodesByColumn($column, $this->tree); 209 | 210 | $importance = 0; 211 | foreach ($nodes as $node) { 212 | $importance += $node->getNodeImpurityDecrease($sampleCount); 213 | } 214 | 215 | $this->featureImportances[$columnName] = $importance; 216 | } 217 | 218 | // Normalize & sort the importances 219 | $total = array_sum($this->featureImportances); 220 | if ($total > 0) { 221 | foreach ($this->featureImportances as &$importance) { 222 | $importance /= $total; 223 | } 224 | 225 | arsort($this->featureImportances); 226 | } 227 | 228 | return $this->featureImportances; 229 | } 230 | 231 | protected function getSplitLeaf(array $records, int $depth = 0): DecisionTreeLeaf 232 | { 233 | $split = $this->getBestSplit($records); 234 | $split->level = $depth; 235 | if ($this->actualDepth < $depth) { 236 | $this->actualDepth = $depth; 237 | } 238 | 239 | // Traverse all records to see if all records belong to the same class, 240 | // otherwise group the records so that we can classify the leaf 241 | // in case maximum depth is reached 242 | $leftRecords = []; 243 | $rightRecords = []; 244 | $remainingTargets = []; 245 | $prevRecord = null; 246 | $allSame = true; 247 | 248 | foreach ($records as $recordNo) { 249 | // Check if the previous record is the same with the current one 250 | $record = $this->samples[$recordNo]; 251 | if ($prevRecord && $prevRecord != $record) { 252 | $allSame = false; 253 | } 254 | 255 | $prevRecord = $record; 256 | 257 | // According to the split criteron, this record will 258 | // belong to either left or the right side in the next split 259 | if ($split->evaluate($record)) { 260 | $leftRecords[] = $recordNo; 261 | } else { 262 | $rightRecords[] = $recordNo; 263 | } 264 | 265 | // Group remaining targets 266 | $target = $this->targets[$recordNo]; 267 | if (!array_key_exists($target, $remainingTargets)) { 268 | $remainingTargets[$target] = 1; 269 | } else { 270 | ++$remainingTargets[$target]; 271 | } 272 | } 273 | 274 | if ($allSame || $depth >= $this->maxDepth || count($remainingTargets) === 1) { 275 | $split->isTerminal = true; 276 | arsort($remainingTargets); 277 | $split->classValue = key($remainingTargets); 278 | } else { 279 | if (!empty($leftRecords)) { 280 | $split->leftLeaf = $this->getSplitLeaf($leftRecords, $depth + 1); 281 | } 282 | 283 | if (!empty($rightRecords)) { 284 | $split->rightLeaf = $this->getSplitLeaf($rightRecords, $depth + 1); 285 | } 286 | } 287 | 288 | return $split; 289 | } 290 | 291 | protected function getBestSplit(array $records): DecisionTreeLeaf 292 | { 293 | $targets = array_intersect_key($this->targets, array_flip($records)); 294 | $samples = array_intersect_key($this->samples, array_flip($records)); 295 | $samples = array_combine($records, $this->preprocess($samples)); 296 | $bestGiniVal = 1; 297 | $bestSplit = null; 298 | $features = $this->getSelectedFeatures(); 299 | foreach ($features as $i) { 300 | $colValues = []; 301 | foreach ($samples as $index => $row) { 302 | $colValues[$index] = $row[$i]; 303 | } 304 | 305 | $counts = array_count_values($colValues); 306 | arsort($counts); 307 | $baseValue = key($counts); 308 | $gini = $this->getGiniIndex($baseValue, $colValues, $targets); 309 | if ($bestSplit === null || $bestGiniVal > $gini) { 310 | $split = new DecisionTreeLeaf(); 311 | $split->value = $baseValue; 312 | $split->giniIndex = $gini; 313 | $split->columnIndex = $i; 314 | $split->isContinuous = $this->columnTypes[$i] === self::CONTINUOUS; 315 | $split->records = $records; 316 | 317 | // If a numeric column is to be selected, then 318 | // the original numeric value and the selected operator 319 | // will also be saved into the leaf for future access 320 | if ($this->columnTypes[$i] === self::CONTINUOUS) { 321 | $matches = []; 322 | preg_match("/^([<>=]{1,2})\s*(.*)/", (string) $split->value, $matches); 323 | $split->operator = $matches[1]; 324 | $split->numericValue = (float) $matches[2]; 325 | } 326 | 327 | $bestSplit = $split; 328 | $bestGiniVal = $gini; 329 | } 330 | } 331 | 332 | return $bestSplit; 333 | } 334 | 335 | /** 336 | * Returns available features/columns to the tree for the decision making 337 | * process.
338 | * 339 | * If a number is given with setNumFeatures() method, then a random selection 340 | * of features up to this number is returned.
341 | * 342 | * If some features are manually selected by use of setSelectedFeatures(), 343 | * then only these features are returned
344 | * 345 | * If any of above methods were not called beforehand, then all features 346 | * are returned by default. 347 | */ 348 | protected function getSelectedFeatures(): array 349 | { 350 | $allFeatures = range(0, $this->featureCount - 1); 351 | if ($this->numUsableFeatures === 0 && empty($this->selectedFeatures)) { 352 | return $allFeatures; 353 | } 354 | 355 | if (!empty($this->selectedFeatures)) { 356 | return $this->selectedFeatures; 357 | } 358 | 359 | $numFeatures = $this->numUsableFeatures; 360 | if ($numFeatures > $this->featureCount) { 361 | $numFeatures = $this->featureCount; 362 | } 363 | 364 | shuffle($allFeatures); 365 | $selectedFeatures = array_slice($allFeatures, 0, $numFeatures); 366 | sort($selectedFeatures); 367 | 368 | return $selectedFeatures; 369 | } 370 | 371 | protected function preprocess(array $samples): array 372 | { 373 | // Detect and convert continuous data column values into 374 | // discrete values by using the median as a threshold value 375 | $columns = []; 376 | for ($i = 0; $i < $this->featureCount; ++$i) { 377 | $values = array_column($samples, $i); 378 | if ($this->columnTypes[$i] == self::CONTINUOUS) { 379 | $median = Mean::median($values); 380 | foreach ($values as &$value) { 381 | if ($value <= $median) { 382 | $value = "<= ${median}"; 383 | } else { 384 | $value = "> ${median}"; 385 | } 386 | } 387 | } 388 | 389 | $columns[] = $values; 390 | } 391 | 392 | // Below method is a strange yet very simple & efficient method 393 | // to get the transpose of a 2D array 394 | return array_map(null, ...$columns); 395 | } 396 | 397 | protected static function isCategoricalColumn(array $columnValues): bool 398 | { 399 | $count = count($columnValues); 400 | 401 | // There are two main indicators that *may* show whether a 402 | // column is composed of discrete set of values: 403 | // 1- Column may contain string values and non-float values 404 | // 2- Number of unique values in the column is only a small fraction of 405 | // all values in that column (Lower than or equal to %20 of all values) 406 | $numericValues = array_filter($columnValues, 'is_numeric'); 407 | $floatValues = array_filter($columnValues, 'is_float'); 408 | if (!empty($floatValues)) { 409 | return false; 410 | } 411 | 412 | if (count($numericValues) !== $count) { 413 | return true; 414 | } 415 | 416 | $distinctValues = array_count_values($columnValues); 417 | 418 | return count($distinctValues) <= $count / 5; 419 | } 420 | 421 | /** 422 | * Used to set predefined features to consider while deciding which column to use for a split 423 | */ 424 | protected function setSelectedFeatures(array $selectedFeatures): void 425 | { 426 | $this->selectedFeatures = $selectedFeatures; 427 | } 428 | 429 | /** 430 | * Collects and returns an array of internal nodes that use the given 431 | * column as a split criterion 432 | */ 433 | protected function getSplitNodesByColumn(int $column, DecisionTreeLeaf $node): array 434 | { 435 | if ($node->isTerminal) { 436 | return []; 437 | } 438 | 439 | $nodes = []; 440 | if ($node->columnIndex === $column) { 441 | $nodes[] = $node; 442 | } 443 | 444 | $lNodes = []; 445 | $rNodes = []; 446 | if ($node->leftLeaf !== null) { 447 | $lNodes = $this->getSplitNodesByColumn($column, $node->leftLeaf); 448 | } 449 | 450 | if ($node->rightLeaf !== null) { 451 | $rNodes = $this->getSplitNodesByColumn($column, $node->rightLeaf); 452 | } 453 | 454 | $nodes = array_merge($nodes, $lNodes, $rNodes); 455 | 456 | return $nodes; 457 | } 458 | 459 | /** 460 | * @return mixed 461 | */ 462 | protected function predictSample(array $sample) 463 | { 464 | $node = $this->tree; 465 | do { 466 | if ($node->isTerminal) { 467 | break; 468 | } 469 | 470 | if ($node->evaluate($sample)) { 471 | $node = $node->leftLeaf; 472 | } else { 473 | $node = $node->rightLeaf; 474 | } 475 | } while ($node); 476 | 477 | return $node !== null ? $node->classValue : $this->labels[0]; 478 | } 479 | } 480 | -------------------------------------------------------------------------------- /joko-dm/Datamining/data/breast-cancer.csv: -------------------------------------------------------------------------------- 1 | clumpThickness,UoCSz,UoCSh,MA,SECZ,bareNuc,BlandChro,NormalNucl,Mitoses,Class 2 | 5,1,1,1,2,1,3,1,1,2 3 | 5,4,4,5,7,10,3,2,1,2 4 | 3,1,1,1,2,2,3,1,1,2 5 | 6,8,8,1,3,4,3,7,1,2 6 | 4,1,1,3,2,1,3,1,1,2 7 | 8,10,10,8,7,10,9,7,1,4 8 | 1,1,1,1,2,10,3,1,1,2 9 | 2,1,2,1,2,1,3,1,1,2 10 | 2,1,1,1,2,1,1,1,5,2 11 | 4,2,1,1,2,1,2,1,1,2 12 | 1,1,1,1,1,1,3,1,1,2 13 | 2,1,1,1,2,1,2,1,1,2 14 | 5,3,3,3,2,3,4,4,1,4 15 | 1,1,1,1,2,3,3,1,1,2 16 | 8,7,5,10,7,9,5,5,4,4 17 | 7,4,6,4,6,1,4,3,1,4 18 | 4,1,1,1,2,1,2,1,1,2 19 | 4,1,1,1,2,1,3,1,1,2 20 | 10,7,7,6,4,10,4,1,2,4 21 | 6,1,1,1,2,1,3,1,1,2 22 | 7,3,2,10,5,10,5,4,4,4 23 | 10,5,5,3,6,7,7,10,1,4 24 | 3,1,1,1,2,1,2,1,1,2 25 | 8,4,5,1,2,?,7,3,1,4 26 | 1,1,1,1,2,1,3,1,1,2 27 | 5,2,3,4,2,7,3,6,1,4 28 | 3,2,1,1,1,1,2,1,1,2 29 | 5,1,1,1,2,1,2,1,1,2 30 | 2,1,1,1,2,1,2,1,1,2 31 | 1,1,3,1,2,1,1,1,1,2 32 | 3,1,1,1,1,1,2,1,1,2 33 | 2,1,1,1,2,1,3,1,1,2 34 | 10,7,7,3,8,5,7,4,3,4 35 | 2,1,1,2,2,1,3,1,1,2 36 | 3,1,2,1,2,1,2,1,1,2 37 | 2,1,1,1,2,1,2,1,1,2 38 | 10,10,10,8,6,1,8,9,1,4 39 | 6,2,1,1,1,1,7,1,1,2 40 | 5,4,4,9,2,10,5,6,1,4 41 | 2,5,3,3,6,7,7,5,1,4 42 | 6,6,6,9,6,?,7,8,1,2 43 | 10,4,3,1,3,3,6,5,2,4 44 | 6,10,10,2,8,10,7,3,3,4 45 | 5,6,5,6,10,1,3,1,1,4 46 | 10,10,10,4,8,1,8,10,1,4 47 | 1,1,1,1,2,1,2,1,2,2 48 | 3,7,7,4,4,9,4,8,1,4 49 | 1,1,1,1,2,1,2,1,1,2 50 | 4,1,1,3,2,1,3,1,1,2 51 | 7,8,7,2,4,8,3,8,2,4 52 | 9,5,8,1,2,3,2,1,5,4 53 | 5,3,3,4,2,4,3,4,1,4 54 | 10,3,6,2,3,5,4,10,2,4 55 | 5,5,5,8,10,8,7,3,7,4 56 | 10,5,5,6,8,8,7,1,1,4 57 | 10,6,6,3,4,5,3,6,1,4 58 | 8,10,10,1,3,6,3,9,1,4 59 | 8,2,4,1,5,1,5,4,4,4 60 | 5,2,3,1,6,10,5,1,1,4 61 | 9,5,5,2,2,2,5,1,1,4 62 | 5,3,5,5,3,3,4,10,1,4 63 | 1,1,1,1,2,2,2,1,1,2 64 | 9,10,10,1,10,8,3,3,1,4 65 | 6,3,4,1,5,2,3,9,1,4 66 | 1,1,1,1,2,1,2,1,1,2 67 | 10,4,2,1,3,2,4,3,10,4 68 | 4,1,1,1,2,1,3,1,1,2 69 | 5,3,4,1,8,10,4,9,1,4 70 | 8,3,8,3,4,9,8,9,8,4 71 | 1,1,1,1,2,1,3,2,1,2 72 | 5,1,3,1,2,1,2,1,1,2 73 | 6,10,2,8,10,2,7,8,10,4 74 | 1,3,3,2,2,1,7,2,1,2 75 | 9,4,5,10,6,10,4,8,1,4 76 | 10,6,4,1,3,4,3,2,3,4 77 | 1,1,2,1,2,2,4,2,1,2 78 | 1,1,4,1,2,1,2,1,1,2 79 | 5,3,1,2,2,1,2,1,1,2 80 | 3,1,1,1,2,3,3,1,1,2 81 | 2,1,1,1,3,1,2,1,1,2 82 | 2,2,2,1,1,1,7,1,1,2 83 | 4,1,1,2,2,1,2,1,1,2 84 | 5,2,1,1,2,1,3,1,1,2 85 | 3,1,1,1,2,2,7,1,1,2 86 | 3,5,7,8,8,9,7,10,7,4 87 | 5,10,6,1,10,4,4,10,10,4 88 | 3,3,6,4,5,8,4,4,1,4 89 | 3,6,6,6,5,10,6,8,3,4 90 | 4,1,1,1,2,1,3,1,1,2 91 | 2,1,1,2,3,1,2,1,1,2 92 | 1,1,1,1,2,1,3,1,1,2 93 | 3,1,1,2,2,1,1,1,1,2 94 | 4,1,1,1,2,1,3,1,1,2 95 | 1,1,1,1,2,1,2,1,1,2 96 | 2,1,1,1,2,1,3,1,1,2 97 | 1,1,1,1,2,1,3,1,1,2 98 | 2,1,1,2,2,1,1,1,1,2 99 | 5,1,1,1,2,1,3,1,1,2 100 | 9,6,9,2,10,6,2,9,10,4 101 | 7,5,6,10,5,10,7,9,4,4 102 | 10,3,5,1,10,5,3,10,2,4 103 | 2,3,4,4,2,5,2,5,1,4 104 | 4,1,2,1,2,1,3,1,1,2 105 | 8,2,3,1,6,3,7,1,1,4 106 | 10,10,10,10,10,1,8,8,8,4 107 | 7,3,4,4,3,3,3,2,7,4 108 | 10,10,10,8,2,10,4,1,1,4 109 | 1,6,8,10,8,10,5,7,1,4 110 | 1,1,1,1,2,1,2,3,1,2 111 | 6,5,4,4,3,9,7,8,3,4 112 | 1,3,1,2,2,2,5,3,2,2 113 | 8,6,4,3,5,9,3,1,1,4 114 | 10,3,3,10,2,10,7,3,3,4 115 | 10,10,10,3,10,8,8,1,1,4 116 | 3,3,2,1,2,3,3,1,1,2 117 | 1,1,1,1,2,5,1,1,1,2 118 | 8,3,3,1,2,2,3,2,1,2 119 | 4,5,5,10,4,10,7,5,8,4 120 | 1,1,1,1,4,3,1,1,1,2 121 | 3,2,1,1,2,2,3,1,1,2 122 | 1,1,2,2,2,1,3,1,1,2 123 | 4,2,1,1,2,2,3,1,1,2 124 | 10,10,10,2,10,10,5,3,3,4 125 | 5,3,5,1,8,10,5,3,1,4 126 | 5,4,6,7,9,7,8,10,1,4 127 | 1,1,1,1,2,1,2,1,1,2 128 | 7,5,3,7,4,10,7,5,5,4 129 | 3,1,1,1,2,1,3,1,1,2 130 | 8,3,5,4,5,10,1,6,2,4 131 | 1,1,1,1,10,1,1,1,1,2 132 | 5,1,3,1,2,1,2,1,1,2 133 | 2,1,1,1,2,1,3,1,1,2 134 | 5,10,8,10,8,10,3,6,3,4 135 | 3,1,1,1,2,1,2,2,1,2 136 | 3,1,1,1,3,1,2,1,1,2 137 | 5,1,1,1,2,2,3,3,1,2 138 | 4,1,1,1,2,1,2,1,1,2 139 | 3,1,1,1,2,1,1,1,1,2 140 | 4,1,2,1,2,1,2,1,1,2 141 | 1,1,1,1,1,?,2,1,1,2 142 | 3,1,1,1,2,1,1,1,1,2 143 | 2,1,1,1,2,1,1,1,1,2 144 | 9,5,5,4,4,5,4,3,3,4 145 | 1,1,1,1,2,5,1,1,1,2 146 | 2,1,1,1,2,1,2,1,1,2 147 | 1,1,3,1,2,?,2,1,1,2 148 | 3,4,5,2,6,8,4,1,1,4 149 | 1,1,1,1,3,2,2,1,1,2 150 | 3,1,1,3,8,1,5,8,1,2 151 | 8,8,7,4,10,10,7,8,7,4 152 | 1,1,1,1,1,1,3,1,1,2 153 | 7,2,4,1,6,10,5,4,3,4 154 | 10,10,8,6,4,5,8,10,1,4 155 | 4,1,1,1,2,3,1,1,1,2 156 | 1,1,1,1,2,1,1,1,1,2 157 | 5,5,5,6,3,10,3,1,1,4 158 | 1,2,2,1,2,1,2,1,1,2 159 | 2,1,1,1,2,1,3,1,1,2 160 | 1,1,2,1,3,?,1,1,1,2 161 | 9,9,10,3,6,10,7,10,6,4 162 | 10,7,7,4,5,10,5,7,2,4 163 | 4,1,1,1,2,1,3,2,1,2 164 | 3,1,1,1,2,1,3,1,1,2 165 | 1,1,1,2,1,3,1,1,7,2 166 | 5,1,1,1,2,?,3,1,1,2 167 | 4,1,1,1,2,2,3,2,1,2 168 | 5,6,7,8,8,10,3,10,3,4 169 | 10,8,10,10,6,1,3,1,10,4 170 | 3,1,1,1,2,1,3,1,1,2 171 | 1,1,1,2,1,1,1,1,1,2 172 | 3,1,1,1,2,1,1,1,1,2 173 | 1,1,1,1,2,1,3,1,1,2 174 | 1,1,1,1,2,1,2,1,1,2 175 | 6,10,10,10,8,10,10,10,7,4 176 | 8,6,5,4,3,10,6,1,1,4 177 | 5,8,7,7,10,10,5,7,1,4 178 | 2,1,1,1,2,1,3,1,1,2 179 | 5,10,10,3,8,1,5,10,3,4 180 | 4,1,1,1,2,1,3,1,1,2 181 | 5,3,3,3,6,10,3,1,1,4 182 | 1,1,1,1,1,1,3,1,1,2 183 | 1,1,1,1,2,1,1,1,1,2 184 | 6,1,1,1,2,1,3,1,1,2 185 | 5,8,8,8,5,10,7,8,1,4 186 | 8,7,6,4,4,10,5,1,1,4 187 | 2,1,1,1,1,1,3,1,1,2 188 | 1,5,8,6,5,8,7,10,1,4 189 | 10,5,6,10,6,10,7,7,10,4 190 | 5,8,4,10,5,8,9,10,1,4 191 | 1,2,3,1,2,1,3,1,1,2 192 | 10,10,10,8,6,8,7,10,1,4 193 | 7,5,10,10,10,10,4,10,3,4 194 | 5,1,1,1,2,1,2,1,1,2 195 | 1,1,1,1,2,1,3,1,1,2 196 | 3,1,1,1,2,1,3,1,1,2 197 | 4,1,1,1,2,1,3,1,1,2 198 | 8,4,4,5,4,7,7,8,2,2 199 | 5,1,1,4,2,1,3,1,1,2 200 | 1,1,1,1,2,1,1,1,1,2 201 | 3,1,1,1,2,1,2,1,1,2 202 | 9,7,7,5,5,10,7,8,3,4 203 | 10,8,8,4,10,10,8,1,1,4 204 | 1,1,1,1,2,1,3,1,1,2 205 | 5,1,1,1,2,1,3,1,1,2 206 | 1,1,1,1,2,1,3,1,1,2 207 | 5,10,10,9,6,10,7,10,5,4 208 | 10,10,9,3,7,5,3,5,1,4 209 | 1,1,1,1,1,1,3,1,1,2 210 | 1,1,1,1,1,1,3,1,1,2 211 | 5,1,1,1,1,1,3,1,1,2 212 | 8,10,10,10,5,10,8,10,6,4 213 | 8,10,8,8,4,8,7,7,1,4 214 | 1,1,1,1,2,1,3,1,1,2 215 | 10,10,10,10,7,10,7,10,4,4 216 | 10,10,10,10,3,10,10,6,1,4 217 | 8,7,8,7,5,5,5,10,2,4 218 | 1,1,1,1,2,1,2,1,1,2 219 | 1,1,1,1,2,1,3,1,1,2 220 | 6,10,7,7,6,4,8,10,2,4 221 | 6,1,3,1,2,1,3,1,1,2 222 | 1,1,1,2,2,1,3,1,1,2 223 | 10,6,4,3,10,10,9,10,1,4 224 | 4,1,1,3,1,5,2,1,1,4 225 | 7,5,6,3,3,8,7,4,1,4 226 | 10,5,5,6,3,10,7,9,2,4 227 | 1,1,1,1,2,1,2,1,1,2 228 | 10,5,7,4,4,10,8,9,1,4 229 | 8,9,9,5,3,5,7,7,1,4 230 | 1,1,1,1,1,1,3,1,1,2 231 | 10,10,10,3,10,10,9,10,1,4 232 | 7,4,7,4,3,7,7,6,1,4 233 | 6,8,7,5,6,8,8,9,2,4 234 | 8,4,6,3,3,1,4,3,1,2 235 | 10,4,5,5,5,10,4,1,1,4 236 | 3,3,2,1,3,1,3,6,1,2 237 | 3,1,4,1,2,?,3,1,1,2 238 | 10,8,8,2,8,10,4,8,10,4 239 | 9,8,8,5,6,2,4,10,4,4 240 | 8,10,10,8,6,9,3,10,10,4 241 | 10,4,3,2,3,10,5,3,2,4 242 | 5,1,3,3,2,2,2,3,1,2 243 | 3,1,1,3,1,1,3,1,1,2 244 | 2,1,1,1,2,1,3,1,1,2 245 | 1,1,1,1,2,5,5,1,1,2 246 | 1,1,1,1,2,1,3,1,1,2 247 | 5,1,1,2,2,2,3,1,1,2 248 | 8,10,10,8,5,10,7,8,1,4 249 | 8,4,4,1,2,9,3,3,1,4 250 | 4,1,1,1,2,1,3,6,1,2 251 | 3,1,1,1,2,?,3,1,1,2 252 | 1,2,2,1,2,1,1,1,1,2 253 | 10,4,4,10,2,10,5,3,3,4 254 | 6,3,3,5,3,10,3,5,3,2 255 | 6,10,10,2,8,10,7,3,3,4 256 | 9,10,10,1,10,8,3,3,1,4 257 | 5,6,6,2,4,10,3,6,1,4 258 | 3,1,1,1,2,1,1,1,1,2 259 | 3,1,1,1,2,1,2,1,1,2 260 | 3,1,1,1,2,1,3,1,1,2 261 | 5,7,7,1,5,8,3,4,1,2 262 | 10,5,8,10,3,10,5,1,3,4 263 | 5,10,10,6,10,10,10,6,5,4 264 | 8,8,9,4,5,10,7,8,1,4 265 | 10,4,4,10,6,10,5,5,1,4 266 | 7,9,4,10,10,3,5,3,3,4 267 | 5,1,4,1,2,1,3,2,1,2 268 | 10,10,6,3,3,10,4,3,2,4 269 | 3,3,5,2,3,10,7,1,1,4 270 | 10,8,8,2,3,4,8,7,8,4 271 | 1,1,1,1,2,1,3,1,1,2 272 | 8,4,7,1,3,10,3,9,2,4 273 | 5,1,1,1,2,1,3,1,1,2 274 | 3,3,5,2,3,10,7,1,1,4 275 | 7,2,4,1,3,4,3,3,1,4 276 | 3,1,1,1,2,1,3,2,1,2 277 | 3,1,3,1,2,?,2,1,1,2 278 | 3,1,1,1,2,1,2,1,1,2 279 | 1,1,1,1,2,1,2,1,1,2 280 | 1,1,1,1,2,1,3,1,1,2 281 | 10,5,7,3,3,7,3,3,8,4 282 | 3,1,1,1,2,1,3,1,1,2 283 | 2,1,1,2,2,1,3,1,1,2 284 | 1,4,3,10,4,10,5,6,1,4 285 | 10,4,6,1,2,10,5,3,1,4 286 | 7,4,5,10,2,10,3,8,2,4 287 | 8,10,10,10,8,10,10,7,3,4 288 | 10,10,10,10,10,10,4,10,10,4 289 | 3,1,1,1,3,1,2,1,1,2 290 | 6,1,3,1,4,5,5,10,1,4 291 | 5,6,6,8,6,10,4,10,4,4 292 | 1,1,1,1,2,1,1,1,1,2 293 | 1,1,1,1,2,1,3,1,1,2 294 | 8,8,8,1,2,?,6,10,1,4 295 | 10,4,4,6,2,10,2,3,1,4 296 | 1,1,1,1,2,?,2,1,1,2 297 | 5,5,7,8,6,10,7,4,1,4 298 | 5,3,4,3,4,5,4,7,1,2 299 | 5,4,3,1,2,?,2,3,1,2 300 | 8,2,1,1,5,1,1,1,1,2 301 | 9,1,2,6,4,10,7,7,2,4 302 | 8,4,10,5,4,4,7,10,1,4 303 | 1,1,1,1,2,1,3,1,1,2 304 | 10,10,10,7,9,10,7,10,10,4 305 | 1,1,1,1,2,1,3,1,1,2 306 | 8,3,4,9,3,10,3,3,1,4 307 | 10,8,4,4,4,10,3,10,4,4 308 | 1,1,1,1,2,1,3,1,1,2 309 | 1,1,1,1,2,1,3,1,1,2 310 | 7,8,7,6,4,3,8,8,4,4 311 | 3,1,1,1,2,5,5,1,1,2 312 | 2,1,1,1,3,1,2,1,1,2 313 | 1,1,1,1,2,1,1,1,1,2 314 | 8,6,4,10,10,1,3,5,1,4 315 | 1,1,1,1,2,1,1,1,1,2 316 | 1,1,1,1,1,1,2,1,1,2 317 | 4,6,5,6,7,?,4,9,1,2 318 | 5,5,5,2,5,10,4,3,1,4 319 | 6,8,7,8,6,8,8,9,1,4 320 | 1,1,1,1,5,1,3,1,1,2 321 | 4,4,4,4,6,5,7,3,1,2 322 | 7,6,3,2,5,10,7,4,6,4 323 | 3,1,1,1,2,?,3,1,1,2 324 | 3,1,1,1,2,1,3,1,1,2 325 | 5,4,6,10,2,10,4,1,1,4 326 | 1,1,1,1,2,1,3,1,1,2 327 | 3,2,2,1,2,1,2,3,1,2 328 | 10,1,1,1,2,10,5,4,1,4 329 | 1,1,1,1,2,1,2,1,1,2 330 | 8,10,3,2,6,4,3,10,1,4 331 | 10,4,6,4,5,10,7,1,1,4 332 | 10,4,7,2,2,8,6,1,1,4 333 | 5,1,1,1,2,1,3,1,2,2 334 | 5,2,2,2,2,1,2,2,1,2 335 | 5,4,6,6,4,10,4,3,1,4 336 | 8,6,7,3,3,10,3,4,2,4 337 | 1,1,1,1,2,1,1,1,1,2 338 | 6,5,5,8,4,10,3,4,1,4 339 | 1,1,1,1,2,1,3,1,1,2 340 | 1,1,1,1,1,1,2,1,1,2 341 | 8,5,5,5,2,10,4,3,1,4 342 | 10,3,3,1,2,10,7,6,1,4 343 | 1,1,1,1,2,1,3,1,1,2 344 | 2,1,1,1,2,1,1,1,1,2 345 | 1,1,1,1,2,1,1,1,1,2 346 | 7,6,4,8,10,10,9,5,3,4 347 | 1,1,1,1,2,1,1,1,1,2 348 | 5,2,2,2,3,1,1,3,1,2 349 | 1,1,1,1,1,1,1,3,1,2 350 | 3,4,4,10,5,1,3,3,1,4 351 | 4,2,3,5,3,8,7,6,1,4 352 | 5,1,1,3,2,1,1,1,1,2 353 | 2,1,1,1,2,1,3,1,1,2 354 | 3,4,5,3,7,3,4,6,1,2 355 | 2,7,10,10,7,10,4,9,4,4 356 | 1,1,1,1,2,1,2,1,1,2 357 | 4,1,1,1,3,1,2,2,1,2 358 | 5,3,3,1,3,3,3,3,3,4 359 | 8,10,10,7,10,10,7,3,8,4 360 | 8,10,5,3,8,4,4,10,3,4 361 | 10,3,5,4,3,7,3,5,3,4 362 | 6,10,10,10,10,10,8,10,10,4 363 | 3,10,3,10,6,10,5,1,4,4 364 | 3,2,2,1,4,3,2,1,1,2 365 | 4,4,4,2,2,3,2,1,1,2 366 | 2,1,1,1,2,1,3,1,1,2 367 | 2,1,1,1,2,1,2,1,1,2 368 | 6,10,10,10,8,10,7,10,7,4 369 | 5,8,8,10,5,10,8,10,3,4 370 | 1,1,3,1,2,1,1,1,1,2 371 | 1,1,3,1,1,1,2,1,1,2 372 | 4,3,2,1,3,1,2,1,1,2 373 | 1,1,3,1,2,1,1,1,1,2 374 | 4,1,2,1,2,1,2,1,1,2 375 | 5,1,1,2,2,1,2,1,1,2 376 | 3,1,2,1,2,1,2,1,1,2 377 | 1,1,1,1,2,1,1,1,1,2 378 | 1,1,1,1,2,1,2,1,1,2 379 | 1,1,1,1,1,1,2,1,1,2 380 | 3,1,1,4,3,1,2,2,1,2 381 | 5,3,4,1,4,1,3,1,1,2 382 | 1,1,1,1,2,1,1,1,1,2 383 | 10,6,3,6,4,10,7,8,4,4 384 | 3,2,2,2,2,1,3,2,1,2 385 | 2,1,1,1,2,1,1,1,1,2 386 | 2,1,1,1,2,1,1,1,1,2 387 | 3,3,2,2,3,1,1,2,3,2 388 | 7,6,6,3,2,10,7,1,1,4 389 | 5,3,3,2,3,1,3,1,1,2 390 | 2,1,1,1,2,1,2,2,1,2 391 | 5,1,1,1,3,2,2,2,1,2 392 | 1,1,1,2,2,1,2,1,1,2 393 | 10,8,7,4,3,10,7,9,1,4 394 | 3,1,1,1,2,1,2,1,1,2 395 | 1,1,1,1,1,1,1,1,1,2 396 | 1,2,3,1,2,1,2,1,1,2 397 | 3,1,1,1,2,1,2,1,1,2 398 | 3,1,1,1,2,1,3,1,1,2 399 | 4,1,1,1,2,1,1,1,1,2 400 | 3,2,1,1,2,1,2,2,1,2 401 | 1,2,3,1,2,1,1,1,1,2 402 | 3,10,8,7,6,9,9,3,8,4 403 | 3,1,1,1,2,1,1,1,1,2 404 | 5,3,3,1,2,1,2,1,1,2 405 | 3,1,1,1,2,4,1,1,1,2 406 | 1,2,1,3,2,1,1,2,1,2 407 | 1,1,1,1,2,1,2,1,1,2 408 | 4,2,2,1,2,1,2,1,1,2 409 | 1,1,1,1,2,1,2,1,1,2 410 | 2,3,2,2,2,2,3,1,1,2 411 | 3,1,2,1,2,1,2,1,1,2 412 | 1,1,1,1,2,1,2,1,1,2 413 | 1,1,1,1,1,?,2,1,1,2 414 | 10,10,10,6,8,4,8,5,1,4 415 | 5,1,2,1,2,1,3,1,1,2 416 | 8,5,6,2,3,10,6,6,1,4 417 | 3,3,2,6,3,3,3,5,1,2 418 | 8,7,8,5,10,10,7,2,1,4 419 | 1,1,1,1,2,1,2,1,1,2 420 | 5,2,2,2,2,2,3,2,2,2 421 | 2,3,1,1,5,1,1,1,1,2 422 | 3,2,2,3,2,3,3,1,1,2 423 | 10,10,10,7,10,10,8,2,1,4 424 | 4,3,3,1,2,1,3,3,1,2 425 | 5,1,3,1,2,1,2,1,1,2 426 | 3,1,1,1,2,1,1,1,1,2 427 | 9,10,10,10,10,10,10,10,1,4 428 | 5,3,6,1,2,1,1,1,1,2 429 | 8,7,8,2,4,2,5,10,1,4 430 | 1,1,1,1,2,1,2,1,1,2 431 | 2,1,1,1,2,1,2,1,1,2 432 | 1,3,1,1,2,1,2,2,1,2 433 | 5,1,1,3,4,1,3,2,1,2 434 | 5,1,1,1,2,1,2,2,1,2 435 | 3,2,2,3,2,1,1,1,1,2 436 | 6,9,7,5,5,8,4,2,1,2 437 | 10,8,10,1,3,10,5,1,1,4 438 | 10,10,10,1,6,1,2,8,1,4 439 | 4,1,1,1,2,1,1,1,1,2 440 | 4,1,3,3,2,1,1,1,1,2 441 | 5,1,1,1,2,1,1,1,1,2 442 | 10,4,3,10,4,10,10,1,1,4 443 | 5,2,2,4,2,4,1,1,1,2 444 | 1,1,1,3,2,3,1,1,1,2 445 | 1,1,1,1,2,2,1,1,1,2 446 | 5,1,1,6,3,1,2,1,1,2 447 | 2,1,1,1,2,1,1,1,1,2 448 | 1,1,1,1,2,1,1,1,1,2 449 | 5,1,1,1,2,1,1,1,1,2 450 | 1,1,1,1,1,1,1,1,1,2 451 | 5,7,9,8,6,10,8,10,1,4 452 | 4,1,1,3,1,1,2,1,1,2 453 | 5,1,1,1,2,1,1,1,1,2 454 | 3,1,1,3,2,1,1,1,1,2 455 | 4,5,5,8,6,10,10,7,1,4 456 | 2,3,1,1,3,1,1,1,1,2 457 | 10,2,2,1,2,6,1,1,2,4 458 | 10,6,5,8,5,10,8,6,1,4 459 | 8,8,9,6,6,3,10,10,1,4 460 | 5,1,2,1,2,1,1,1,1,2 461 | 5,1,3,1,2,1,1,1,1,2 462 | 5,1,1,3,2,1,1,1,1,2 463 | 3,1,1,1,2,5,1,1,1,2 464 | 6,1,1,3,2,1,1,1,1,2 465 | 4,1,1,1,2,1,1,2,1,2 466 | 4,1,1,1,2,1,1,1,1,2 467 | 10,9,8,7,6,4,7,10,3,4 468 | 10,6,6,2,4,10,9,7,1,4 469 | 6,6,6,5,4,10,7,6,2,4 470 | 4,1,1,1,2,1,1,1,1,2 471 | 1,1,2,1,2,1,2,1,1,2 472 | 3,1,1,1,1,1,2,1,1,2 473 | 6,1,1,3,2,1,1,1,1,2 474 | 6,1,1,1,1,1,1,1,1,2 475 | 4,1,1,1,2,1,1,1,1,2 476 | 5,1,1,1,2,1,1,1,1,2 477 | 3,1,1,1,2,1,1,1,1,2 478 | 4,1,2,1,2,1,1,1,1,2 479 | 4,1,1,1,2,1,1,1,1,2 480 | 5,2,1,1,2,1,1,1,1,2 481 | 4,8,7,10,4,10,7,5,1,4 482 | 5,1,1,1,1,1,1,1,1,2 483 | 5,3,2,4,2,1,1,1,1,2 484 | 9,10,10,10,10,5,10,10,10,4 485 | 8,7,8,5,5,10,9,10,1,4 486 | 5,1,2,1,2,1,1,1,1,2 487 | 1,1,1,3,1,3,1,1,1,2 488 | 3,1,1,1,1,1,2,1,1,2 489 | 10,10,10,10,6,10,8,1,5,4 490 | 3,6,4,10,3,3,3,4,1,4 491 | 6,3,2,1,3,4,4,1,1,4 492 | 1,1,1,1,2,1,1,1,1,2 493 | 5,8,9,4,3,10,7,1,1,4 494 | 4,1,1,1,1,1,2,1,1,2 495 | 5,10,10,10,6,10,6,5,2,4 496 | 5,1,2,10,4,5,2,1,1,2 497 | 3,1,1,1,1,1,2,1,1,2 498 | 1,1,1,1,1,1,1,1,1,2 499 | 4,2,1,1,2,1,1,1,1,2 500 | 4,1,1,1,2,1,2,1,1,2 501 | 4,1,1,1,2,1,2,1,1,2 502 | 6,1,1,1,2,1,3,1,1,2 503 | 4,1,1,1,2,1,2,1,1,2 504 | 4,1,1,2,2,1,2,1,1,2 505 | 4,1,1,1,2,1,3,1,1,2 506 | 1,1,1,1,2,1,1,1,1,2 507 | 3,3,1,1,2,1,1,1,1,2 508 | 8,10,10,10,7,5,4,8,7,4 509 | 1,1,1,1,2,4,1,1,1,2 510 | 5,1,1,1,2,1,1,1,1,2 511 | 2,1,1,1,2,1,1,1,1,2 512 | 1,1,1,1,2,1,1,1,1,2 513 | 5,1,1,1,2,1,2,1,1,2 514 | 5,1,1,1,2,1,1,1,1,2 515 | 3,1,1,1,1,1,2,1,1,2 516 | 6,6,7,10,3,10,8,10,2,4 517 | 4,10,4,7,3,10,9,10,1,4 518 | 1,1,1,1,1,1,1,1,1,2 519 | 1,1,1,1,1,1,2,1,1,2 520 | 3,1,2,2,2,1,1,1,1,2 521 | 4,7,8,3,4,10,9,1,1,4 522 | 1,1,1,1,3,1,1,1,1,2 523 | 4,1,1,1,3,1,1,1,1,2 524 | 10,4,5,4,3,5,7,3,1,4 525 | 7,5,6,10,4,10,5,3,1,4 526 | 3,1,1,1,2,1,2,1,1,2 527 | 3,1,1,2,2,1,1,1,1,2 528 | 4,1,1,1,2,1,1,1,1,2 529 | 4,1,1,1,2,1,3,1,1,2 530 | 6,1,3,2,2,1,1,1,1,2 531 | 4,1,1,1,1,1,2,1,1,2 532 | 7,4,4,3,4,10,6,9,1,4 533 | 4,2,2,1,2,1,2,1,1,2 534 | 1,1,1,1,1,1,3,1,1,2 535 | 3,1,1,1,2,1,2,1,1,2 536 | 2,1,1,1,2,1,2,1,1,2 537 | 1,1,3,2,2,1,3,1,1,2 538 | 5,1,1,1,2,1,3,1,1,2 539 | 5,1,2,1,2,1,3,1,1,2 540 | 4,1,1,1,2,1,2,1,1,2 541 | 6,1,1,1,2,1,2,1,1,2 542 | 5,1,1,1,2,2,2,1,1,2 543 | 3,1,1,1,2,1,1,1,1,2 544 | 5,3,1,1,2,1,1,1,1,2 545 | 4,1,1,1,2,1,2,1,1,2 546 | 2,1,3,2,2,1,2,1,1,2 547 | 5,1,1,1,2,1,2,1,1,2 548 | 6,10,10,10,4,10,7,10,1,4 549 | 2,1,1,1,1,1,1,1,1,2 550 | 3,1,1,1,1,1,1,1,1,2 551 | 7,8,3,7,4,5,7,8,2,4 552 | 3,1,1,1,2,1,2,1,1,2 553 | 1,1,1,1,2,1,3,1,1,2 554 | 3,2,2,2,2,1,4,2,1,2 555 | 4,4,2,1,2,5,2,1,2,2 556 | 3,1,1,1,2,1,1,1,1,2 557 | 4,3,1,1,2,1,4,8,1,2 558 | 5,2,2,2,1,1,2,1,1,2 559 | 5,1,1,3,2,1,1,1,1,2 560 | 2,1,1,1,2,1,2,1,1,2 561 | 5,1,1,1,2,1,2,1,1,2 562 | 5,1,1,1,2,1,3,1,1,2 563 | 5,1,1,1,2,1,3,1,1,2 564 | 1,1,1,1,2,1,3,1,1,2 565 | 3,1,1,1,2,1,2,1,1,2 566 | 4,1,1,1,2,1,3,2,1,2 567 | 5,7,10,10,5,10,10,10,1,4 568 | 3,1,2,1,2,1,3,1,1,2 569 | 4,1,1,1,2,3,2,1,1,2 570 | 8,4,4,1,6,10,2,5,2,4 571 | 10,10,8,10,6,5,10,3,1,4 572 | 8,10,4,4,8,10,8,2,1,4 573 | 7,6,10,5,3,10,9,10,2,4 574 | 3,1,1,1,2,1,2,1,1,2 575 | 1,1,1,1,2,1,2,1,1,2 576 | 10,9,7,3,4,2,7,7,1,4 577 | 5,1,2,1,2,1,3,1,1,2 578 | 5,1,1,1,2,1,2,1,1,2 579 | 1,1,1,1,2,1,2,1,1,2 580 | 1,1,1,1,2,1,2,1,1,2 581 | 1,1,1,1,2,1,3,1,1,2 582 | 5,1,2,1,2,1,2,1,1,2 583 | 5,7,10,6,5,10,7,5,1,4 584 | 6,10,5,5,4,10,6,10,1,4 585 | 3,1,1,1,2,1,1,1,1,2 586 | 5,1,1,6,3,1,1,1,1,2 587 | 1,1,1,1,2,1,1,1,1,2 588 | 8,10,10,10,6,10,10,10,1,4 589 | 5,1,1,1,2,1,2,2,1,2 590 | 9,8,8,9,6,3,4,1,1,4 591 | 5,1,1,1,2,1,1,1,1,2 592 | 4,10,8,5,4,1,10,1,1,4 593 | 2,5,7,6,4,10,7,6,1,4 594 | 10,3,4,5,3,10,4,1,1,4 595 | 5,1,2,1,2,1,1,1,1,2 596 | 4,8,6,3,4,10,7,1,1,4 597 | 5,1,1,1,2,1,2,1,1,2 598 | 4,1,2,1,2,1,2,1,1,2 599 | 5,1,3,1,2,1,3,1,1,2 600 | 3,1,1,1,2,1,2,1,1,2 601 | 5,2,4,1,1,1,1,1,1,2 602 | 3,1,1,1,2,1,2,1,1,2 603 | 1,1,1,1,1,1,2,1,1,2 604 | 4,1,1,1,2,1,2,1,1,2 605 | 5,4,6,8,4,1,8,10,1,4 606 | 5,3,2,8,5,10,8,1,2,4 607 | 10,5,10,3,5,8,7,8,3,4 608 | 4,1,1,2,2,1,1,1,1,2 609 | 1,1,1,1,2,1,1,1,1,2 610 | 5,10,10,10,10,10,10,1,1,4 611 | 5,1,1,1,2,1,1,1,1,2 612 | 10,4,3,10,3,10,7,1,2,4 613 | 5,10,10,10,5,2,8,5,1,4 614 | 8,10,10,10,6,10,10,10,10,4 615 | 2,3,1,1,2,1,2,1,1,2 616 | 2,1,1,1,1,1,2,1,1,2 617 | 4,1,3,1,2,1,2,1,1,2 618 | 3,1,1,1,2,1,2,1,1,2 619 | 1,1,1,1,1,?,1,1,1,2 620 | 4,1,1,1,2,1,2,1,1,2 621 | 5,1,1,1,2,1,2,1,1,2 622 | 3,1,1,1,2,1,2,1,1,2 623 | 6,3,3,3,3,2,6,1,1,2 624 | 7,1,2,3,2,1,2,1,1,2 625 | 1,1,1,1,2,1,1,1,1,2 626 | 5,1,1,2,1,1,2,1,1,2 627 | 3,1,3,1,3,4,1,1,1,2 628 | 4,6,6,5,7,6,7,7,3,4 629 | 2,1,1,1,2,5,1,1,1,2 630 | 2,1,1,1,2,1,1,1,1,2 631 | 4,1,1,1,2,1,1,1,1,2 632 | 6,2,3,1,2,1,1,1,1,2 633 | 5,1,1,1,2,1,2,1,1,2 634 | 1,1,1,1,2,1,1,1,1,2 635 | 8,7,4,4,5,3,5,10,1,4 636 | 3,1,1,1,2,1,1,1,1,2 637 | 3,1,4,1,2,1,1,1,1,2 638 | 10,10,7,8,7,1,10,10,3,4 639 | 4,2,4,3,2,2,2,1,1,2 640 | 4,1,1,1,2,1,1,1,1,2 641 | 5,1,1,3,2,1,1,1,1,2 642 | 4,1,1,3,2,1,1,1,1,2 643 | 3,1,1,1,2,1,2,1,1,2 644 | 3,1,1,1,2,1,2,1,1,2 645 | 1,1,1,1,2,1,1,1,1,2 646 | 2,1,1,1,2,1,1,1,1,2 647 | 3,1,1,1,2,1,2,1,1,2 648 | 1,2,2,1,2,1,1,1,1,2 649 | 1,1,1,3,2,1,1,1,1,2 650 | 5,10,10,10,10,2,10,10,10,4 651 | 3,1,1,1,2,1,2,1,1,2 652 | 3,1,1,2,3,4,1,1,1,2 653 | 1,2,1,3,2,1,2,1,1,2 654 | 5,1,1,1,2,1,2,2,1,2 655 | 4,1,1,1,2,1,2,1,1,2 656 | 3,1,1,1,2,1,3,1,1,2 657 | 3,1,1,1,2,1,2,1,1,2 658 | 5,1,1,1,2,1,2,1,1,2 659 | 5,4,5,1,8,1,3,6,1,2 660 | 7,8,8,7,3,10,7,2,3,4 661 | 1,1,1,1,2,1,1,1,1,2 662 | 1,1,1,1,2,1,2,1,1,2 663 | 4,1,1,1,2,1,3,1,1,2 664 | 1,1,3,1,2,1,2,1,1,2 665 | 1,1,3,1,2,1,2,1,1,2 666 | 3,1,1,3,2,1,2,1,1,2 667 | 1,1,1,1,2,1,1,1,1,2 668 | 5,2,2,2,2,1,1,1,2,2 669 | 3,1,1,1,2,1,3,1,1,2 670 | 5,7,4,1,6,1,7,10,3,4 671 | 5,10,10,8,5,5,7,10,1,4 672 | 3,10,7,8,5,8,7,4,1,4 673 | 3,2,1,2,2,1,3,1,1,2 674 | 2,1,1,1,2,1,3,1,1,2 675 | 5,3,2,1,3,1,1,1,1,2 676 | 1,1,1,1,2,1,2,1,1,2 677 | 4,1,4,1,2,1,1,1,1,2 678 | 1,1,2,1,2,1,2,1,1,2 679 | 5,1,1,1,2,1,1,1,1,2 680 | 1,1,1,1,2,1,1,1,1,2 681 | 2,1,1,1,2,1,1,1,1,2 682 | 10,10,10,10,5,10,10,10,7,4 683 | 5,10,10,10,4,10,5,6,3,4 684 | 5,1,1,1,2,1,3,2,1,2 685 | 1,1,1,1,2,1,1,1,1,2 686 | 1,1,1,1,2,1,1,1,1,2 687 | 1,1,1,1,2,1,1,1,1,2 688 | 1,1,1,1,2,1,1,1,1,2 689 | 3,1,1,1,2,1,2,3,1,2 690 | 4,1,1,1,2,1,1,1,1,2 691 | 1,1,1,1,2,1,1,1,8,2 692 | 1,1,1,3,2,1,1,1,1,2 693 | 5,10,10,5,4,5,4,4,1,4 694 | 3,1,1,1,2,1,1,1,1,2 695 | 3,1,1,1,2,1,2,1,2,2 696 | 3,1,1,1,3,2,1,1,1,2 697 | 2,1,1,1,2,1,1,1,1,2 698 | 5,10,10,3,7,3,8,10,2,4 699 | 4,8,6,4,3,4,10,6,1,4 700 | 4,8,8,5,4,5,10,4,1,4 701 | -------------------------------------------------------------------------------- /public/css/bootstrap-reboot.min.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["../../scss/bootstrap-reboot.scss","../../scss/_reboot.scss","dist/css/bootstrap-reboot.css","bootstrap-reboot.css","../../scss/mixins/_hover.scss"],"names":[],"mappings":"AAAA;;;;;;ACoBA,ECXA,QADA,SDeE,WAAA,WAGF,KACE,YAAA,WACA,YAAA,KACA,yBAAA,KACA,qBAAA,KACA,mBAAA,UACA,4BAAA,YAKA,cACE,MAAA,aAMJ,QAAA,MAAA,WAAA,OAAA,OAAA,OAAA,OAAA,KAAA,IAAA,QACE,QAAA,MAWF,KACE,OAAA,EACA,YAAA,aAAA,CAAA,kBAAA,CAAA,UAAA,CAAA,MAAA,CAAA,gBAAA,CAAA,KAAA,CAAA,UAAA,CAAA,mBAAA,CAAA,gBAAA,CAAA,iBAAA,CAAA,mBACA,UAAA,KACA,YAAA,IACA,YAAA,IACA,MAAA,QACA,WAAA,KACA,iBAAA,KEvBF,sBFgCE,QAAA,YASF,GACE,WAAA,YACA,OAAA,EACA,SAAA,QAaF,GAAA,GAAA,GAAA,GAAA,GAAA,GACE,WAAA,EACA,cAAA,MAQF,EACE,WAAA,EACA,cAAA,KChDF,0BD0DA,YAEE,gBAAA,UACA,wBAAA,UAAA,OAAA,gBAAA,UAAA,OACA,OAAA,KACA,cAAA,EAGF,QACE,cAAA,KACA,WAAA,OACA,YAAA,QCrDF,GDwDA,GCzDA,GD4DE,WAAA,EACA,cAAA,KAGF,MCxDA,MACA,MAFA,MD6DE,cAAA,EAGF,GACE,YAAA,IAGF,GACE,cAAA,MACA,YAAA,EAGF,WACE,OAAA,EAAA,EAAA,KAGF,IACE,WAAA,OAIF,EC1DA,OD4DE,YAAA,OAIF,MACE,UAAA,IAQF,IChEA,IDkEE,SAAA,SACA,UAAA,IACA,YAAA,EACA,eAAA,SAGF,IAAM,OAAA,OACN,IAAM,IAAA,MAON,EACE,MAAA,QACA,gBAAA,KACA,iBAAA,YACA,6BAAA,QG7LA,QHgME,MAAA,QACA,gBAAA,UAUJ,8BACE,MAAA,QACA,gBAAA,KGzMA,oCAAA,oCH4ME,MAAA,QACA,gBAAA,KANJ,oCAUI,QAAA,EClEJ,KACA,ID0EA,ICzEA,KD6EE,YAAA,cAAA,CAAA,KAAA,CAAA,MAAA,CAAA,QAAA,CAAA,iBAAA,CAAA,aAAA,CAAA,UACA,UAAA,IAGF,IAEE,WAAA,EAEA,cAAA,KAEA,SAAA,KAGA,mBAAA,UAQF,OAEE,OAAA,EAAA,EAAA,KAQF,IACE,eAAA,OACA,aAAA,KAGF,IAGE,SAAA,OACA,eAAA,OAQF,MACE,gBAAA,SAGF,QACE,YAAA,OACA,eAAA,OACA,MAAA,QACA,WAAA,KACA,aAAA,OAGF,GAGE,WAAA,QAQF,MAEE,QAAA,aACA,cAAA,MAMF,OACE,cAAA,EAOF,aACE,QAAA,IAAA,OACA,QAAA,IAAA,KAAA,yBC9GF,ODiHA,MC/GA,SADA,OAEA,SDmHE,OAAA,EACA,YAAA,QACA,UAAA,QACA,YAAA,QAGF,OCjHA,MDmHE,SAAA,QAGF,OCjHA,ODmHE,eAAA,KC7GF,aACA,cDkHA,OCpHA,mBDwHE,mBAAA,OCjHF,gCACA,+BACA,gCDmHA,yBAIE,QAAA,EACA,aAAA,KClHF,qBDqHA,kBAEE,WAAA,WACA,QAAA,EAIF,iBCrHA,2BACA,kBAFA,iBD+HE,mBAAA,QAGF,SACE,SAAA,KAEA,OAAA,SAGF,SAME,UAAA,EAEA,QAAA,EACA,OAAA,EACA,OAAA,EAKF,OACE,QAAA,MACA,MAAA,KACA,UAAA,KACA,QAAA,EACA,cAAA,MACA,UAAA,OACA,YAAA,QACA,MAAA,QACA,YAAA,OAGF,SACE,eAAA,SEnIF,yCDEA,yCDuIE,OAAA,KEpIF,cF4IE,eAAA,KACA,mBAAA,KExIF,4CDEA,yCD+IE,mBAAA,KAQF,6BACE,KAAA,QACA,mBAAA,OAOF,OACE,QAAA,aAGF,QACE,QAAA,UACA,OAAA,QAGF,SACE,QAAA,KErJF,SF2JE,QAAA","sourcesContent":["/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n\n@import \"functions\";\n@import \"variables\";\n@import \"mixins\";\n@import \"reboot\";\n","// stylelint-disable at-rule-no-vendor-prefix, declaration-no-important, selector-no-qualifying-type, property-no-vendor-prefix\n\n// Reboot\n//\n// Normalization of HTML elements, manually forked from Normalize.css to remove\n// styles targeting irrelevant browsers while applying new styles.\n//\n// Normalize is licensed MIT. https://github.com/necolas/normalize.css\n\n\n// Document\n//\n// 1. Change from `box-sizing: content-box` so that `width` is not affected by `padding` or `border`.\n// 2. Change the default font family in all browsers.\n// 3. Correct the line height in all browsers.\n// 4. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS.\n// 5. Setting @viewport causes scrollbars to overlap content in IE11 and Edge, so\n// we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n// 6. Change the default tap highlight to be completely transparent in iOS.\n\n*,\n*::before,\n*::after {\n box-sizing: border-box; // 1\n}\n\nhtml {\n font-family: sans-serif; // 2\n line-height: 1.15; // 3\n -webkit-text-size-adjust: 100%; // 4\n -ms-text-size-adjust: 100%; // 4\n -ms-overflow-style: scrollbar; // 5\n -webkit-tap-highlight-color: rgba($black, 0); // 6\n}\n\n// IE10+ doesn't honor `` in some cases.\n@at-root {\n @-ms-viewport {\n width: device-width;\n }\n}\n\n// stylelint-disable selector-list-comma-newline-after\n// Shim for \"new\" HTML5 structural elements to display correctly (IE10, older browsers)\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Body\n//\n// 1. Remove the margin in all browsers.\n// 2. As a best practice, apply a default `background-color`.\n// 3. Set an explicit initial text-align value so that we can later use the\n// the `inherit` value on things like `` elements.\n\nbody {\n margin: 0; // 1\n font-family: $font-family-base;\n font-size: $font-size-base;\n font-weight: $font-weight-base;\n line-height: $line-height-base;\n color: $body-color;\n text-align: left; // 3\n background-color: $body-bg; // 2\n}\n\n// Suppress the focus outline on elements that cannot be accessed via keyboard.\n// This prevents an unwanted focus outline from appearing around elements that\n// might still respond to pointer events.\n//\n// Credit: https://github.com/suitcss/base\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\n\n// Content grouping\n//\n// 1. Add the correct box sizing in Firefox.\n// 2. Show the overflow in Edge and IE.\n\nhr {\n box-sizing: content-box; // 1\n height: 0; // 1\n overflow: visible; // 2\n}\n\n\n//\n// Typography\n//\n\n// Remove top margins from headings\n//\n// By default, `

`-`

` all receive top and bottom margins. We nuke the top\n// margin for easier control within type scales as it avoids margin collapsing.\n// stylelint-disable selector-list-comma-newline-after\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: $headings-margin-bottom;\n}\n// stylelint-enable selector-list-comma-newline-after\n\n// Reset margins on paragraphs\n//\n// Similarly, the top margin on `

`s get reset. However, we also reset the\n// bottom margin to use `rem` units instead of `em`.\np {\n margin-top: 0;\n margin-bottom: $paragraph-margin-bottom;\n}\n\n// Abbreviations\n//\n// 1. Remove the bottom border in Firefox 39-.\n// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.\n// 3. Add explicit cursor to indicate changed behavior.\n// 4. Duplicate behavior to the data-* attribute for our tooltip plugin\n\nabbr[title],\nabbr[data-original-title] { // 4\n text-decoration: underline; // 2\n text-decoration: underline dotted; // 2\n cursor: help; // 3\n border-bottom: 0; // 1\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: $dt-font-weight;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0; // Undo browser default\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic; // Add the correct font style in Android 4.3-\n}\n\n// stylelint-disable font-weight-notation\nb,\nstrong {\n font-weight: bolder; // Add the correct font weight in Chrome, Edge, and Safari\n}\n// stylelint-enable font-weight-notation\n\nsmall {\n font-size: 80%; // Add the correct font size in all browsers\n}\n\n//\n// Prevent `sub` and `sup` elements from affecting the line height in\n// all browsers.\n//\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub { bottom: -.25em; }\nsup { top: -.5em; }\n\n\n//\n// Links\n//\n\na {\n color: $link-color;\n text-decoration: $link-decoration;\n background-color: transparent; // Remove the gray background on active links in IE 10.\n -webkit-text-decoration-skip: objects; // Remove gaps in links underline in iOS 8+ and Safari 8+.\n\n @include hover {\n color: $link-hover-color;\n text-decoration: $link-hover-decoration;\n }\n}\n\n// And undo these styles for placeholder links/named anchors (without href)\n// which have not been made explicitly keyboard-focusable (without tabindex).\n// It would be more straightforward to just use a[href] in previous block, but that\n// causes specificity issues in many other styles that are too complex to fix.\n// See https://github.com/twbs/bootstrap/issues/19402\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n\n @include hover-focus {\n color: inherit;\n text-decoration: none;\n }\n\n &:focus {\n outline: 0;\n }\n}\n\n\n//\n// Code\n//\n\npre,\ncode,\nkbd,\nsamp {\n font-family: $font-family-monospace;\n font-size: 1em; // Correct the odd `em` font sizing in all browsers.\n}\n\npre {\n // Remove browser default top margin\n margin-top: 0;\n // Reset browser default of `1em` to use `rem`s\n margin-bottom: 1rem;\n // Don't allow content to break outside\n overflow: auto;\n // We have @viewport set which causes scrollbars to overlap content in IE11 and Edge, so\n // we force a non-overlapping, non-auto-hiding scrollbar to counteract.\n -ms-overflow-style: scrollbar;\n}\n\n\n//\n// Figures\n//\n\nfigure {\n // Apply a consistent margin strategy (matches our type styles).\n margin: 0 0 1rem;\n}\n\n\n//\n// Images and content\n//\n\nimg {\n vertical-align: middle;\n border-style: none; // Remove the border on images inside links in IE 10-.\n}\n\nsvg {\n // Workaround for the SVG overflow bug in IE10/11 is still required.\n // See https://github.com/twbs/bootstrap/issues/26878\n overflow: hidden;\n vertical-align: middle;\n}\n\n\n//\n// Tables\n//\n\ntable {\n border-collapse: collapse; // Prevent double borders\n}\n\ncaption {\n padding-top: $table-cell-padding;\n padding-bottom: $table-cell-padding;\n color: $table-caption-color;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n // Matches default `` alignment by inheriting from the ``, or the\n // closest parent with a set `text-align`.\n text-align: inherit;\n}\n\n\n//\n// Forms\n//\n\nlabel {\n // Allow labels to use `margin` for spacing.\n display: inline-block;\n margin-bottom: $label-margin-bottom;\n}\n\n// Remove the default `border-radius` that macOS Chrome adds.\n//\n// Details at https://github.com/twbs/bootstrap/issues/24093\nbutton {\n border-radius: 0;\n}\n\n// Work around a Firefox/IE bug where the transparent `button` background\n// results in a loss of the default `button` focus styles.\n//\n// Credit: https://github.com/suitcss/base/\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0; // Remove the margin in Firefox and Safari\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible; // Show the overflow in Edge\n}\n\nbutton,\nselect {\n text-transform: none; // Remove the inheritance of text transform in Firefox\n}\n\n// 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`\n// controls in Android 4.\n// 2. Correct the inability to style clickable types in iOS and Safari.\nbutton,\nhtml [type=\"button\"], // 1\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button; // 2\n}\n\n// Remove inner border and padding from Firefox, but don't restore the outline like Normalize.\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box; // 1. Add the correct box sizing in IE 10-\n padding: 0; // 2. Remove the padding in IE 10-\n}\n\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n // Remove the default appearance of temporal inputs to avoid a Mobile Safari\n // bug where setting a custom line-height prevents text from being vertically\n // centered within the input.\n // See https://bugs.webkit.org/show_bug.cgi?id=139848\n // and https://github.com/twbs/bootstrap/issues/11266\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto; // Remove the default vertical scrollbar in IE.\n // Textareas should really only resize vertically so they don't break their (horizontal) containers.\n resize: vertical;\n}\n\nfieldset {\n // Browsers set a default `min-width: min-content;` on fieldsets,\n // unlike e.g. `

`s, which have `min-width: 0;` by default.\n // So we reset that to ensure fieldsets behave more like a standard block element.\n // See https://github.com/twbs/bootstrap/issues/12359\n // and https://html.spec.whatwg.org/multipage/#the-fieldset-and-legend-elements\n min-width: 0;\n // Reset the default outline behavior of fieldsets so they don't affect page layout.\n padding: 0;\n margin: 0;\n border: 0;\n}\n\n// 1. Correct the text wrapping in Edge and IE.\n// 2. Correct the color inheritance from `fieldset` elements in IE.\nlegend {\n display: block;\n width: 100%;\n max-width: 100%; // 1\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit; // 2\n white-space: normal; // 1\n}\n\nprogress {\n vertical-align: baseline; // Add the correct vertical alignment in Chrome, Firefox, and Opera.\n}\n\n// Correct the cursor style of increment and decrement buttons in Chrome.\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n // This overrides the extra rounded corners on search inputs in iOS so that our\n // `.form-control` class can properly style them. Note that this cannot simply\n // be added to `.form-control` as it's not specific enough. For details, see\n // https://github.com/twbs/bootstrap/issues/11586.\n outline-offset: -2px; // 2. Correct the outline style in Safari.\n -webkit-appearance: none;\n}\n\n//\n// Remove the inner padding and cancel buttons in Chrome and Safari on macOS.\n//\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n//\n// 1. Correct the inability to style clickable types in iOS and Safari.\n// 2. Change font properties to `inherit` in Safari.\n//\n\n::-webkit-file-upload-button {\n font: inherit; // 2\n -webkit-appearance: button; // 1\n}\n\n//\n// Correct element displays\n//\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item; // Add the correct display in all browsers\n cursor: pointer;\n}\n\ntemplate {\n display: none; // Add the correct display in IE\n}\n\n// Always hide an element with the `hidden` HTML attribute (from PureCSS).\n// Needed for proper display in IE 10-.\n[hidden] {\n display: none !important;\n}\n","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n -webkit-text-decoration: underline dotted;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n/*# sourceMappingURL=bootstrap-reboot.css.map */","/*!\n * Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)\n * Copyright 2011-2018 The Bootstrap Authors\n * Copyright 2011-2018 Twitter, Inc.\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)\n * Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)\n */\n*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\nhtml {\n font-family: sans-serif;\n line-height: 1.15;\n -webkit-text-size-adjust: 100%;\n -ms-text-size-adjust: 100%;\n -ms-overflow-style: scrollbar;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\n@-ms-viewport {\n width: device-width;\n}\n\narticle, aside, figcaption, figure, footer, header, hgroup, main, nav, section {\n display: block;\n}\n\nbody {\n margin: 0;\n font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\";\n font-size: 1rem;\n font-weight: 400;\n line-height: 1.5;\n color: #212529;\n text-align: left;\n background-color: #fff;\n}\n\n[tabindex=\"-1\"]:focus {\n outline: 0 !important;\n}\n\nhr {\n box-sizing: content-box;\n height: 0;\n overflow: visible;\n}\n\nh1, h2, h3, h4, h5, h6 {\n margin-top: 0;\n margin-bottom: 0.5rem;\n}\n\np {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nabbr[title],\nabbr[data-original-title] {\n text-decoration: underline;\n text-decoration: underline dotted;\n cursor: help;\n border-bottom: 0;\n}\n\naddress {\n margin-bottom: 1rem;\n font-style: normal;\n line-height: inherit;\n}\n\nol,\nul,\ndl {\n margin-top: 0;\n margin-bottom: 1rem;\n}\n\nol ol,\nul ul,\nol ul,\nul ol {\n margin-bottom: 0;\n}\n\ndt {\n font-weight: 700;\n}\n\ndd {\n margin-bottom: .5rem;\n margin-left: 0;\n}\n\nblockquote {\n margin: 0 0 1rem;\n}\n\ndfn {\n font-style: italic;\n}\n\nb,\nstrong {\n font-weight: bolder;\n}\n\nsmall {\n font-size: 80%;\n}\n\nsub,\nsup {\n position: relative;\n font-size: 75%;\n line-height: 0;\n vertical-align: baseline;\n}\n\nsub {\n bottom: -.25em;\n}\n\nsup {\n top: -.5em;\n}\n\na {\n color: #007bff;\n text-decoration: none;\n background-color: transparent;\n -webkit-text-decoration-skip: objects;\n}\n\na:hover {\n color: #0056b3;\n text-decoration: underline;\n}\n\na:not([href]):not([tabindex]) {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {\n color: inherit;\n text-decoration: none;\n}\n\na:not([href]):not([tabindex]):focus {\n outline: 0;\n}\n\npre,\ncode,\nkbd,\nsamp {\n font-family: SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", \"Courier New\", monospace;\n font-size: 1em;\n}\n\npre {\n margin-top: 0;\n margin-bottom: 1rem;\n overflow: auto;\n -ms-overflow-style: scrollbar;\n}\n\nfigure {\n margin: 0 0 1rem;\n}\n\nimg {\n vertical-align: middle;\n border-style: none;\n}\n\nsvg {\n overflow: hidden;\n vertical-align: middle;\n}\n\ntable {\n border-collapse: collapse;\n}\n\ncaption {\n padding-top: 0.75rem;\n padding-bottom: 0.75rem;\n color: #6c757d;\n text-align: left;\n caption-side: bottom;\n}\n\nth {\n text-align: inherit;\n}\n\nlabel {\n display: inline-block;\n margin-bottom: 0.5rem;\n}\n\nbutton {\n border-radius: 0;\n}\n\nbutton:focus {\n outline: 1px dotted;\n outline: 5px auto -webkit-focus-ring-color;\n}\n\ninput,\nbutton,\nselect,\noptgroup,\ntextarea {\n margin: 0;\n font-family: inherit;\n font-size: inherit;\n line-height: inherit;\n}\n\nbutton,\ninput {\n overflow: visible;\n}\n\nbutton,\nselect {\n text-transform: none;\n}\n\nbutton,\nhtml [type=\"button\"],\n[type=\"reset\"],\n[type=\"submit\"] {\n -webkit-appearance: button;\n}\n\nbutton::-moz-focus-inner,\n[type=\"button\"]::-moz-focus-inner,\n[type=\"reset\"]::-moz-focus-inner,\n[type=\"submit\"]::-moz-focus-inner {\n padding: 0;\n border-style: none;\n}\n\ninput[type=\"radio\"],\ninput[type=\"checkbox\"] {\n box-sizing: border-box;\n padding: 0;\n}\n\ninput[type=\"date\"],\ninput[type=\"time\"],\ninput[type=\"datetime-local\"],\ninput[type=\"month\"] {\n -webkit-appearance: listbox;\n}\n\ntextarea {\n overflow: auto;\n resize: vertical;\n}\n\nfieldset {\n min-width: 0;\n padding: 0;\n margin: 0;\n border: 0;\n}\n\nlegend {\n display: block;\n width: 100%;\n max-width: 100%;\n padding: 0;\n margin-bottom: .5rem;\n font-size: 1.5rem;\n line-height: inherit;\n color: inherit;\n white-space: normal;\n}\n\nprogress {\n vertical-align: baseline;\n}\n\n[type=\"number\"]::-webkit-inner-spin-button,\n[type=\"number\"]::-webkit-outer-spin-button {\n height: auto;\n}\n\n[type=\"search\"] {\n outline-offset: -2px;\n -webkit-appearance: none;\n}\n\n[type=\"search\"]::-webkit-search-cancel-button,\n[type=\"search\"]::-webkit-search-decoration {\n -webkit-appearance: none;\n}\n\n::-webkit-file-upload-button {\n font: inherit;\n -webkit-appearance: button;\n}\n\noutput {\n display: inline-block;\n}\n\nsummary {\n display: list-item;\n cursor: pointer;\n}\n\ntemplate {\n display: none;\n}\n\n[hidden] {\n display: none !important;\n}\n\n/*# sourceMappingURL=bootstrap-reboot.css.map */","// Hover mixin and `$enable-hover-media-query` are deprecated.\n//\n// Originally added during our alphas and maintained during betas, this mixin was\n// designed to prevent `:hover` stickiness on iOS-an issue where hover styles\n// would persist after initial touch.\n//\n// For backward compatibility, we've kept these mixins and updated them to\n// always return their regular pseudo-classes instead of a shimmed media query.\n//\n// Issue: https://github.com/twbs/bootstrap/issues/25195\n\n@mixin hover {\n &:hover { @content; }\n}\n\n@mixin hover-focus {\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin plain-hover-focus {\n &,\n &:hover,\n &:focus {\n @content;\n }\n}\n\n@mixin hover-focus-active {\n &:hover,\n &:focus,\n &:active {\n @content;\n }\n}\n"]} -------------------------------------------------------------------------------- /public/css/bootstrap-grid.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Grid v4.1.3 (https://getbootstrap.com/) 3 | * Copyright 2011-2018 The Bootstrap Authors 4 | * Copyright 2011-2018 Twitter, Inc. 5 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 6 | */@-ms-viewport{width:device-width}html{box-sizing:border-box;-ms-overflow-style:scrollbar}*,::after,::before{box-sizing:inherit}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-10,.col-11,.col-12,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-auto,.col-lg,.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-auto,.col-md,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-auto,.col-sm,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.333333%}.offset-2{margin-left:16.666667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.333333%}.offset-5{margin-left:41.666667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.333333%}.offset-8{margin-left:66.666667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.333333%}.offset-11{margin-left:91.666667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-sm-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-sm-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-sm-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-sm-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.333333%}.offset-sm-2{margin-left:16.666667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.333333%}.offset-sm-5{margin-left:41.666667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.333333%}.offset-sm-8{margin-left:66.666667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.333333%}.offset-sm-11{margin-left:91.666667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-md-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-md-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-md-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-md-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.333333%}.offset-md-2{margin-left:16.666667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.333333%}.offset-md-5{margin-left:41.666667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.333333%}.offset-md-8{margin-left:66.666667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.333333%}.offset-md-11{margin-left:91.666667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-lg-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-lg-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-lg-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-lg-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.333333%}.offset-lg-2{margin-left:16.666667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.333333%}.offset-lg-5{margin-left:41.666667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.333333%}.offset-lg-8{margin-left:66.666667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.333333%}.offset-lg-11{margin-left:91.666667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.333333%;flex:0 0 8.333333%;max-width:8.333333%}.col-xl-2{-ms-flex:0 0 16.666667%;flex:0 0 16.666667%;max-width:16.666667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.333333%;flex:0 0 33.333333%;max-width:33.333333%}.col-xl-5{-ms-flex:0 0 41.666667%;flex:0 0 41.666667%;max-width:41.666667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.333333%;flex:0 0 58.333333%;max-width:58.333333%}.col-xl-8{-ms-flex:0 0 66.666667%;flex:0 0 66.666667%;max-width:66.666667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.333333%;flex:0 0 83.333333%;max-width:83.333333%}.col-xl-11{-ms-flex:0 0 91.666667%;flex:0 0 91.666667%;max-width:91.666667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.333333%}.offset-xl-2{margin-left:16.666667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.333333%}.offset-xl-5{margin-left:41.666667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.333333%}.offset-xl-8{margin-left:66.666667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.333333%}.offset-xl-11{margin-left:91.666667%}}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}} 7 | /*# sourceMappingURL=bootstrap-grid.min.css.map */ --------------------------------------------------------------------------------