$points */ public function __construct( public array $points ) { if (empty($points)) { throw FrameworkException::create( ErrorCode::VAL_INVALID_FORMAT, 'Chart data cannot be empty' ); } // Validate all points are DataPoint instances foreach ($points as $point) { if (!$point instanceof DataPoint) { throw FrameworkException::create( ErrorCode::VAL_INVALID_FORMAT, 'All chart data points must be DataPoint instances' ); } } } /** * Create from array of values with labels */ public static function fromArray(array $data): self { $points = []; foreach ($data as $label => $value) { $points[] = new DataPoint((string) $label, (float) $value); } return new self($points); } /** * Get point count */ public function count(): int { return count($this->points); } /** * Get minimum value */ public function getMinValue(): float { return min(array_map(fn (DataPoint $p) => $p->value, $this->points)); } /** * Get maximum value */ public function getMaxValue(): float { return max(array_map(fn (DataPoint $p) => $p->value, $this->points)); } /** * Get sum of all values */ public function getSum(): float { return array_sum(array_map(fn (DataPoint $p) => $p->value, $this->points)); } /** * Get average value */ public function getAverage(): float { return $this->getSum() / $this->count(); } /** * Normalize values to 0-1 range */ public function normalize(): self { $min = $this->getMinValue(); $max = $this->getMaxValue(); $range = $max - $min; if ($range === 0.0) { return $this; } $normalized = array_map( fn (DataPoint $p) => new DataPoint( $p->label, ($p->value - $min) / $range ), $this->points ); return new self($normalized); } public function toArray(): array { return array_map(fn (DataPoint $p) => $p->toArray(), $this->points); } }