package mockmodel import ( "hash/fnv" "math" "strconv" "strings" "time" ) type SeriesStyle int const ( StyleSpiky SeriesStyle = iota StylePlateau StyleFlat ) type StorageCapacitySeries struct { Usage []float64 Used []float64 Avail []float64 Total []float64 } type metricProfile int const ( profileCompute metricProfile = iota profileMemory profileDiskIO profileNetwork profileCapacity profileThermal profileFlat ) type metricRoleModifiers struct { steadyStateScale float64 activityScale float64 diurnalScale float64 burstScale float64 maintenanceScale float64 memoryBaseScale float64 memoryDriftScale float64 storageDriftScale float64 thermalScale float64 } func normalizeMetricRole(role string) string { role = strings.ToLower(strings.TrimSpace(role)) if role == "" { return "general" } return role } func metricRoleProfile(role string) metricRoleModifiers { switch normalizeMetricRole(role) { case "web": return metricRoleModifiers{steadyStateScale: 0.95, activityScale: 0.95, diurnalScale: 1.35, burstScale: 1.15, maintenanceScale: 0.7, memoryBaseScale: 0.92, memoryDriftScale: 0.95, storageDriftScale: 0.88, thermalScale: 1.0} case "api": return metricRoleModifiers{steadyStateScale: 1.0, activityScale: 1.1, diurnalScale: 1.15, burstScale: 1.2, maintenanceScale: 0.85, memoryBaseScale: 1.0, memoryDriftScale: 1.05, storageDriftScale: 0.92, thermalScale: 1.04} case "database": return metricRoleModifiers{steadyStateScale: 1.22, activityScale: 0.82, diurnalScale: 0.7, burstScale: 0.8, maintenanceScale: 1.2, memoryBaseScale: 1.35, memoryDriftScale: 0.72, storageDriftScale: 1.12, thermalScale: 1.08} case "cache": return metricRoleModifiers{steadyStateScale: 0.82, activityScale: 1.02, diurnalScale: 0.95, burstScale: 1.35, maintenanceScale: 0.65, memoryBaseScale: 1.45, memoryDriftScale: 0.75, storageDriftScale: 0.72, thermalScale: 1.03} case "queue", "batch": return metricRoleModifiers{steadyStateScale: 0.9, activityScale: 0.95, diurnalScale: 0.85, burstScale: 1.45, maintenanceScale: 1.35, memoryBaseScale: 0.9, memoryDriftScale: 1.15, storageDriftScale: 1.0, thermalScale: 1.06} case "monitoring": return metricRoleModifiers{steadyStateScale: 1.05, activityScale: 0.8, diurnalScale: 0.85, burstScale: 0.75, maintenanceScale: 0.9, memoryBaseScale: 1.2, memoryDriftScale: 0.7, storageDriftScale: 1.05, thermalScale: 0.98} case "backup": return metricRoleModifiers{steadyStateScale: 0.58, activityScale: 0.45, diurnalScale: 0.5, burstScale: 0.7, maintenanceScale: 1.95, memoryBaseScale: 0.72, memoryDriftScale: 0.65, storageDriftScale: 1.2, thermalScale: 0.95} case "storage": return metricRoleModifiers{steadyStateScale: 1.08, activityScale: 0.65, diurnalScale: 0.55, burstScale: 0.6, maintenanceScale: 1.35, memoryBaseScale: 0.96, memoryDriftScale: 0.62, storageDriftScale: 1.28, thermalScale: 1.02} case "ingress": return metricRoleModifiers{steadyStateScale: 0.88, activityScale: 1.0, diurnalScale: 1.25, burstScale: 1.3, maintenanceScale: 0.65, memoryBaseScale: 0.82, memoryDriftScale: 0.88, storageDriftScale: 0.8, thermalScale: 1.0} case "ci": return metricRoleModifiers{steadyStateScale: 0.86, activityScale: 1.08, diurnalScale: 0.92, burstScale: 1.5, maintenanceScale: 1.25, memoryBaseScale: 1.02, memoryDriftScale: 1.22, storageDriftScale: 0.95, thermalScale: 1.08} case "media": return metricRoleModifiers{steadyStateScale: 1.0, activityScale: 1.05, diurnalScale: 1.45, burstScale: 1.28, maintenanceScale: 0.8, memoryBaseScale: 1.05, memoryDriftScale: 0.98, storageDriftScale: 1.0, thermalScale: 1.12} case "security": return metricRoleModifiers{steadyStateScale: 0.84, activityScale: 0.9, diurnalScale: 0.95, burstScale: 1.0, maintenanceScale: 0.85, memoryBaseScale: 0.9, memoryDriftScale: 0.72, storageDriftScale: 0.85, thermalScale: 1.0} default: return metricRoleModifiers{steadyStateScale: 1.0, activityScale: 1.0, diurnalScale: 1.0, burstScale: 1.0, maintenanceScale: 1.0, memoryBaseScale: 1.0, memoryDriftScale: 1.0, storageDriftScale: 1.0, thermalScale: 1.0} } } func NormalizeBlendWeight(weight float64, step, reference time.Duration) float64 { if weight <= 0 { return 0.01 } if weight >= 1 { return 1 } if step <= 0 || reference <= 0 || step == reference { return weight } ratio := float64(step) / float64(reference) normalized := 1 - math.Pow(1-weight, ratio) return clampFloat(normalized, 0.0005, 0.999) } func StyleSpeed(style SeriesStyle) float64 { switch style { case StylePlateau: return 0.5 case StyleFlat: return 0.12 default: return 1.0 } } func ValueAt(seed uint64, min, max float64, speed float64, at time.Time) float64 { return valueAtProfile(seed, min, max, profileFromSpeed(speed), at) } func ValueAtMetric(seed uint64, min, max float64, metric string, speed float64, at time.Time) float64 { return ValueAtMetricWithRole(seed, min, max, metric, speed, "", at) } func ValueAtMetricWithRole( seed uint64, min, max float64, metric string, speed float64, role string, at time.Time, ) float64 { return valueAtProfileWithRole(seed, min, max, profileFromMetric(metric, profileFromSpeed(speed)), role, at) } func SeriesForTimestamps(current float64, timestamps []time.Time, seed uint64, min, max float64, style SeriesStyle) []float64 { return seriesForProfile(current, timestamps, seed, min, max, profileFromStyle(style)) } func SeriesForMetricTimestamps( current float64, timestamps []time.Time, seed uint64, min float64, max float64, metric string, style SeriesStyle, ) []float64 { return SeriesForMetricTimestampsWithRole(current, timestamps, seed, min, max, metric, style, "") } func SeriesForMetricTimestampsWithRole( current float64, timestamps []time.Time, seed uint64, min float64, max float64, metric string, style SeriesStyle, role string, ) []float64 { return seriesForProfileWithRole(current, timestamps, seed, min, max, profileFromMetric(metric, profileFromStyle(style)), role) } func StorageCapacitySeriesForTimestamps( currentUsed float64, currentTotal float64, timestamps []time.Time, seed uint64, ) StorageCapacitySeries { return StorageCapacitySeriesForTimestampsWithRole(currentUsed, currentTotal, timestamps, seed, "") } func StorageCapacitySeriesForTimestampsWithRole( currentUsed float64, currentTotal float64, timestamps []time.Time, seed uint64, role string, ) StorageCapacitySeries { if len(timestamps) == 0 { return StorageCapacitySeries{} } total := math.Max(0, currentTotal) if total <= 0 { zeros := make([]float64, len(timestamps)) return StorageCapacitySeries{ Usage: zeros, Used: append([]float64(nil), zeros...), Avail: append([]float64(nil), zeros...), Total: append([]float64(nil), zeros...), } } used := clampFloat(currentUsed, 0, total) currentUsage := clampFloat((used/total)*100, 0, 100) usageSeries := SeriesForMetricTimestampsWithRole(currentUsage, timestamps, seed, 0, 100, "usage", StyleFlat, role) usedSeries := make([]float64, len(timestamps)) availSeries := make([]float64, len(timestamps)) totalSeries := make([]float64, len(timestamps)) for i, usage := range usageSeries { totalSeries[i] = total occupancy := clampFloat(usage/100, 0, 1) usedSeries[i] = total * occupancy availSeries[i] = total - usedSeries[i] } last := len(timestamps) - 1 usageSeries[last] = currentUsage usedSeries[last] = used availSeries[last] = total - used totalSeries[last] = total return StorageCapacitySeries{ Usage: usageSeries, Used: usedSeries, Avail: availSeries, Total: totalSeries, } } func seriesForProfile( current float64, timestamps []time.Time, seed uint64, min float64, max float64, profile metricProfile, ) []float64 { return seriesForProfileWithRole(current, timestamps, seed, min, max, profile, "") } func seriesForProfileWithRole( current float64, timestamps []time.Time, seed uint64, min float64, max float64, profile metricProfile, role string, ) []float64 { if len(timestamps) == 0 { return nil } current = clampFloat(current, min, max) values := make([]float64, len(timestamps)) for i, ts := range timestamps { values[i] = valueAtProfileWithRole(seed, min, max, profile, role, ts) } now := timestamps[len(timestamps)-1] shift := current - values[len(values)-1] halfLife := anchorHalfLife(profile) for i, ts := range timestamps { age := now.Sub(ts) if age < 0 { age = 0 } values[i] = clampFloat(values[i]+shift*anchorWeight(age, halfLife), min, max) } values[len(values)-1] = current return values } func valueAtProfile(seed uint64, min, max float64, profile metricProfile, at time.Time) float64 { return valueAtProfileWithRole(seed, min, max, profile, "", at) } func valueAtProfileWithRole(seed uint64, min, max float64, profile metricProfile, role string, at time.Time) float64 { span := math.Max(1, max-min) modifiers := metricRoleProfile(role) switch profile { case profileMemory: return clampFloat(memoryValue(seed, min, span, modifiers, at), min, max) case profileDiskIO: return clampFloat(diskIOValue(seed, min, span, modifiers, at), min, max) case profileNetwork: return clampFloat(networkValue(seed, min, span, modifiers, at), min, max) case profileCapacity: return clampFloat(capacityValue(seed, min, span, modifiers, at), min, max) case profileThermal: return clampFloat(thermalValue(seed, min, span, modifiers, at), min, max) case profileFlat: return clampFloat(flatValue(seed, min, span, modifiers, at), min, max) default: return clampFloat(computeValue(seed, min, span, modifiers, at), min, max) } } func computeValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { activity := clamp01(scheduledActivity(seed, at) * modifiers.activityScale) utilBias := 0.04 + unitFloat(seed, "cpu-util", 0)*0.12 platformBias := 0.03 + unitFloat(seed, "cpu-platform", 0)*0.08 sustained := span * modifiers.steadyStateScale * (utilBias + platformBias + activity*(0.10+unitFloat(seed, "cpu-active", 0)*0.24)) diurnal := span * modifiers.diurnalScale * scheduledWave(seed, "cpu-diurnal", at, 0.04) trend := span * slowTrend(seed, "cpu-trend", at, 6*time.Hour, 24*time.Hour, 0.08) chatter := span * (0.008 + activity*0.018) * positiveFloat(seed, "cpu-chatter", at.Unix(), 5*60) spikes := span * modifiers.burstScale * (0.08 + activity*0.22 + unitFloat(seed, "cpu-spike-amp", 0)*0.10) * eventPulse(seed, "cpu-spike", at, 8*time.Minute, 0.02+activity*0.06) batch := span * modifiers.burstScale * (0.05 + activity*0.18) * eventPulse(seed, "cpu-batch", at, 36*time.Minute, 0.012+activity*0.03) noise := span * 0.004 * interpolatedCenteredFloat(seed, "cpu-noise", at.Unix(), 90) return min + sustained + diurnal + trend + chatter + spikes + batch + noise } func memoryValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { activity := clamp01(scheduledActivity(seed, at) * modifiers.activityScale) base := span * modifiers.memoryBaseScale * (0.20 + unitFloat(seed, "mem-base", 0)*0.42) workingSet := span * (0.03 + activity*0.07*modifiers.activityScale) plateau := span * 0.11 * modifiers.memoryDriftScale * plateauComponent(seed, "mem-plateau", at, 6*time.Hour, 0.16) trend := span * 0.05 * modifiers.memoryDriftScale * plateauComponent(seed, "mem-trend", at, 24*time.Hour, 0.10) hotset := span * (0.006 + activity*0.018) * positiveFloat(seed, "mem-hotset", at.Unix(), 20*60) reclaim := span * (0.02 + unitFloat(seed, "mem-reclaim", 0)*0.05) * eventPulse(seed, "mem-reclaim", at, 12*time.Hour, 0.04) noise := span * 0.0015 * interpolatedCenteredFloat(seed, "mem-noise", at.Unix(), 5*60) return min + base + workingSet + plateau + trend + hotset - reclaim + noise } func diskIOValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { activity := clamp01(scheduledActivity(seed, at) * modifiers.activityScale) backup := maintenanceWindow(seed, at) * modifiers.maintenanceScale base := span * modifiers.steadyStateScale * (0.015 + unitFloat(seed, "io-base", 0)*0.035) sustained := span * activity * modifiers.steadyStateScale * (0.03 + unitFloat(seed, "io-active", 0)*0.07) backupLoad := span * backup * (0.05 + unitFloat(seed, "io-backup", 0)*0.10) bursts := span * modifiers.burstScale * (0.10 + activity*0.12 + backup*0.18) * eventPulse(seed, "io-burst", at, 12*time.Minute, 0.04+activity*0.05+backup*0.03) sweeps := span * modifiers.maintenanceScale * (0.06 + backup*0.18) * eventPulse(seed, "io-sweep", at, 48*time.Minute, 0.02+backup*0.05) chatter := span * (0.006 + activity*0.010) * positiveFloat(seed, "io-chatter", at.Unix(), 8*60) noise := span * 0.003 * interpolatedCenteredFloat(seed, "io-noise", at.Unix(), 120) return min + base + sustained + backupLoad + bursts + sweeps + chatter + noise } func networkValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { activity := clamp01(scheduledActivity(seed, at) * modifiers.activityScale) backup := maintenanceWindow(seed+17, at) * 0.45 * modifiers.maintenanceScale base := span * modifiers.steadyStateScale * (0.012 + unitFloat(seed, "net-base", 0)*0.04) flow := span * activity * modifiers.steadyStateScale * (0.05 + unitFloat(seed, "net-flow", 0)*0.09) diurnal := span * modifiers.diurnalScale * scheduledWave(seed, "net-diurnal", at, 0.05) bursts := span * modifiers.burstScale * (0.08 + activity*0.16 + backup*0.12) * eventPulse(seed, "net-burst", at, 7*time.Minute, 0.05+activity*0.07) sessions := span * modifiers.burstScale * (0.05 + activity*0.14) * eventPulse(seed, "net-session", at, 28*time.Minute, 0.025+activity*0.03+backup*0.02) chatter := span * (0.008 + activity*0.012) * positiveFloat(seed, "net-chatter", at.Unix(), 4*60) noise := span * 0.003 * interpolatedCenteredFloat(seed, "net-noise", at.Unix(), 90) return min + base + flow + diurnal + bursts + sessions + chatter + noise } func capacityValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { base := span * modifiers.steadyStateScale * (0.20 + unitFloat(seed, "capacity-base", 0)*0.58) cycle := capacityCycle(seed, at) drift := span * 0.05 * modifiers.storageDriftScale * plateauComponent(seed, "capacity-drift", at, 36*time.Hour, 0.08) usageWave := span * modifiers.storageDriftScale * (0.07 + unitFloat(seed, "capacity-amp", 0)*0.10) * (cycle - 0.40) noise := span * 0.001 * interpolatedCenteredFloat(seed, "capacity-noise", at.Unix(), 30*60) return min + base + usageWave + drift + noise } func flatValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { base := span * modifiers.steadyStateScale * (0.24 + unitFloat(seed, "flat-base", 0)*0.44) drift := span * 0.05 * modifiers.storageDriftScale * plateauComponent(seed, "flat-drift", at, 18*time.Hour, 0.12) micro := span * 0.012 * interpolatedCenteredFloat(seed, "flat-micro", at.Unix(), 30*60) noise := span * 0.001 * interpolatedCenteredFloat(seed, "flat-noise", at.Unix(), 10*60) return min + base + drift + micro + noise } func thermalValue(seed uint64, min, span float64, modifiers metricRoleModifiers, at time.Time) float64 { base := span * modifiers.thermalScale * (0.18 + unitFloat(seed, "thermal-base", 0)*0.22) drift := span * 0.02 * plateauComponent(seed, "thermal-drift", at, 24*time.Hour, 0.06) diurnal := span * 0.012 * scheduledWave(seed, "thermal-diurnal", at, 1.0) chatter := span * 0.004 * interpolatedCenteredFloat(seed, "thermal-chatter", at.Unix(), 45*60) noise := span * 0.0007 * interpolatedCenteredFloat(seed, "thermal-noise", at.Unix(), 15*60) return min + base + drift + diurnal + chatter + noise } func profileFromMetric(metric string, fallback metricProfile) metricProfile { switch strings.ToLower(strings.TrimSpace(metric)) { case "cpu": return profileCompute case "memory": return profileMemory case "diskread", "diskwrite", "diskio", "iops": return profileDiskIO case "netin", "netout", "network", "rx", "tx": return profileNetwork case "disk", "usage", "used", "avail", "total", "free", "storage", "capacity": return profileCapacity case "temperature", "temp", "smart_temp": return profileThermal default: return fallback } } func profileFromStyle(style SeriesStyle) metricProfile { switch style { case StylePlateau: return profileMemory case StyleFlat: return profileCapacity default: return profileCompute } } func profileFromSpeed(speed float64) metricProfile { switch { case speed >= 0.8: return profileCompute case speed >= 0.3: return profileMemory default: return profileCapacity } } func anchorHalfLife(profile metricProfile) time.Duration { switch profile { case profileMemory: return 18 * time.Hour case profileCapacity: return 72 * time.Hour case profileThermal, profileFlat: return 96 * time.Hour case profileDiskIO, profileNetwork: return 10 * time.Hour default: return 6 * time.Hour } } func anchorWeight(age, halfLife time.Duration) float64 { if age <= 0 || halfLife <= 0 { return 1 } return math.Exp(-math.Ln2 * float64(age) / float64(halfLife)) } func scheduledActivity(seed uint64, at time.Time) float64 { utc := at.UTC() hour := float64(utc.Hour()) + float64(utc.Minute())/60 + float64(utc.Second())/3600 weekday := utc.Weekday() mode := seed % 6 intensity := 0.82 + unitFloat(seed, "schedule-intensity", 0)*0.36 weekendScale := 0.35 + unitFloat(seed, "weekend-scale", 0)*0.45 var activity float64 switch mode { case 0: // office-heavy activity = 0.05 + 0.72*wrappedPeak(hour, 10.0, 2.2) + 0.58*wrappedPeak(hour, 14.5, 2.6) if weekday == time.Saturday || weekday == time.Sunday { activity *= weekendScale } case 1: // always-on SaaS activity = 0.38 + 0.18*wrappedPeak(hour, 11.0, 4.5) + 0.14*wrappedPeak(hour, 20.0, 3.6) case 2: // evening-heavy activity = 0.07 + 0.24*wrappedPeak(hour, 12.5, 3.0) + 0.72*wrappedPeak(hour, 20.0, 2.8) if weekday == time.Saturday || weekday == time.Sunday { activity *= 1.15 } case 3: // batch/night activity = 0.09 + 0.64*wrappedPeak(hour, 1.5, 2.1) + 0.34*wrappedPeak(hour, 5.0, 2.0) case 4: // follow-the-sun activity = 0.26 + 0.22*wrappedPeak(hour, 8.0, 4.0) + 0.20*wrappedPeak(hour, 16.0, 4.0) + 0.12*wrappedPeak(hour, 22.0, 3.3) default: // weekend/media activity = 0.08 + 0.28*wrappedPeak(hour, 11.5, 3.3) + 0.62*wrappedPeak(hour, 21.0, 3.0) if weekday == time.Saturday || weekday == time.Sunday { activity *= 1.22 } else { activity *= 0.85 } } return clamp01(activity * intensity) } func scheduledWave(seed uint64, label string, at time.Time, amplitude float64) float64 { activity := scheduledActivity(seed, at) return amplitude * (activity - 0.42) } func maintenanceWindow(seed uint64, at time.Time) float64 { hour := float64(at.UTC().Hour()) + float64(at.UTC().Minute())/60 center := unitFloat(seed, "maintenance-hour", 0) * 24 width := 0.9 + unitFloat(seed, "maintenance-width", 0)*1.7 strength := 0.35 + unitFloat(seed, "maintenance-strength", 0)*0.65 return wrappedPeak(hour, center, width) * strength } func slowTrend(seed uint64, label string, at time.Time, shortWindow, longWindow time.Duration, amplitude float64) float64 { short := interpolatedCenteredFloat(seed, label+"-short", at.Unix(), int64(shortWindow/time.Second)) long := interpolatedCenteredFloat(seed, label+"-long", at.Unix(), int64(longWindow/time.Second)) return amplitude * (short*0.45 + long*0.55) } func plateauComponent(seed uint64, label string, at time.Time, window time.Duration, transitionFraction float64) float64 { seconds := int64(window / time.Second) if seconds <= 0 { return centeredFloat(seed, label, 0) } if transitionFraction <= 0 { transitionFraction = 0.1 } if transitionFraction >= 1 { transitionFraction = 0.95 } bucket := floorDiv(at.Unix(), seconds) progress := float64(mod(at.Unix(), seconds)) / float64(seconds) current := centeredFloat(seed, label, bucket) startTransition := 1 - transitionFraction if progress <= startTransition { return current } next := centeredFloat(seed, label, bucket+1) t := smoothstep((progress - startTransition) / transitionFraction) return current*(1-t) + next*t } func capacityCycle(seed uint64, at time.Time) float64 { cycleHours := 42 + unitFloat(seed, "capacity-cycle", 0)*120 offset := unitFloat(seed, "capacity-offset", 0) * cycleHours phase := math.Mod(float64(at.Unix())/3600+offset, cycleHours) / cycleHours if phase < 0.9 { return phase / 0.9 } return 1 - smoothstep((phase-0.9)/0.1) } func eventPulse(seed uint64, label string, at time.Time, window time.Duration, probability float64) float64 { seconds := int64(window / time.Second) if seconds <= 0 { return 0 } probability = clampFloat(probability, 0, 0.92) bucket := floorDiv(at.Unix(), seconds) if unitFloat(seed, label+"-gate", bucket) >= probability { return 0 } progress := float64(mod(at.Unix(), seconds)) / float64(seconds) riseFraction := 0.08 + unitFloat(seed, label+"-rise", bucket)*0.18 if progress <= riseFraction { return progress / riseFraction } decay := (progress - riseFraction) / math.Max(1e-6, 1-riseFraction) power := 1.2 + unitFloat(seed, label+"-power", bucket)*2.2 return math.Pow(1-decay, power) } func positiveFloat(seed uint64, label string, unixSeconds, window int64) float64 { return math.Max(0, interpolatedCenteredFloat(seed, label, unixSeconds, window)) } func wrappedPeak(hour, center, width float64) float64 { if width <= 0 { width = 1 } delta := math.Abs(hour - center) if delta > 12 { delta = 24 - delta } x := delta / width return math.Exp(-0.5 * x * x) } func clamp01(value float64) float64 { return clampFloat(value, 0, 1) } func interpolatedCenteredFloat(seed uint64, label string, unixSeconds, window int64) float64 { if window <= 0 { return centeredFloat(seed, label, 0) } bucket := floorDiv(unixSeconds, window) progress := float64(mod(unixSeconds, window)) / float64(window) a := centeredFloat(seed, label, bucket) b := centeredFloat(seed, label, bucket+1) t := smoothstep(progress) return a*(1-t) + b*t } func centeredFloat(seed uint64, label string, bucket int64) float64 { return unitFloat(seed, label, bucket)*2 - 1 } func unitFloat(seed uint64, label string, bucket int64) float64 { hash := hashKey(seed, label, bucket) const mask = uint64(1<<53) - 1 return float64(hash&mask) / float64(mask) } func hashKey(seed uint64, label string, bucket int64) uint64 { h := fnv.New64a() _, _ = h.Write([]byte(strconv.FormatUint(seed, 10))) _, _ = h.Write([]byte{0}) _, _ = h.Write([]byte(label)) _, _ = h.Write([]byte{0}) _, _ = h.Write([]byte(strconv.FormatInt(bucket, 10))) return h.Sum64() } func smoothstep(t float64) float64 { if t <= 0 { return 0 } if t >= 1 { return 1 } return t * t * (3 - 2*t) } func floorDiv(value, divisor int64) int64 { if divisor == 0 { return 0 } quotient := value / divisor remainder := value % divisor if remainder != 0 && ((remainder < 0) != (divisor < 0)) { quotient-- } return quotient } func mod(value, divisor int64) int64 { if divisor == 0 { return 0 } remainder := value % divisor if remainder < 0 { remainder += divisor } return remainder } func clampFloat(value, min, max float64) float64 { if value < min { return min } if value > max { return max } return value }