mirror of
https://github.com/allaunthefox/Research-Stack.git
synced 2026-07-31 03:05:21 +00:00
210 lines
8.9 KiB
Text
210 lines
8.9 KiB
Text
import Semantics.FixedPoint
|
|
|
|
namespace Semantics.UnitConversion
|
|
|
|
/-- Unit type for metric/imperial conversions -/
|
|
inductive Unit
|
|
-- Length units
|
|
| meter
|
|
| kilometer
|
|
| foot
|
|
| yard
|
|
| mile
|
|
| inch
|
|
| centimeter
|
|
| millimeter
|
|
-- Temperature units
|
|
| celsius
|
|
| kelvin
|
|
| fahrenheit
|
|
-- Volume units
|
|
| liter
|
|
| gallon
|
|
| cubic_meter
|
|
| cubic_foot
|
|
-- Mass units
|
|
| kilogram
|
|
| pound
|
|
| ounce
|
|
| gram
|
|
-- Pressure units
|
|
| pascal
|
|
| bar
|
|
| psi
|
|
-- Energy units
|
|
| joule
|
|
| calorie
|
|
| btu
|
|
deriving Repr, DecidableEq, BEq
|
|
|
|
instance : ToString Unit where
|
|
toString
|
|
| .meter => "meter"
|
|
| .kilometer => "kilometer"
|
|
| .foot => "foot"
|
|
| .yard => "yard"
|
|
| .mile => "mile"
|
|
| .inch => "inch"
|
|
| .centimeter => "centimeter"
|
|
| .millimeter => "millimeter"
|
|
| .celsius => "celsius"
|
|
| .kelvin => "kelvin"
|
|
| .fahrenheit => "fahrenheit"
|
|
| .liter => "liter"
|
|
| .gallon => "gallon"
|
|
| .cubic_meter => "cubic_meter"
|
|
| .cubic_foot => "cubic_foot"
|
|
| .kilogram => "kilogram"
|
|
| .pound => "pound"
|
|
| .ounce => "ounce"
|
|
| .gram => "gram"
|
|
| .pascal => "pascal"
|
|
| .bar => "bar"
|
|
| .psi => "psi"
|
|
| .joule => "joule"
|
|
| .calorie => "calorie"
|
|
| .btu => "btu"
|
|
|
|
/-- Conversion ratio from source to target unit -/
|
|
structure ConversionRatio where
|
|
source : Unit
|
|
target : Unit
|
|
ratio : Q0_16 -- conversion factor (dimensionless scalar)
|
|
deriving Repr, DecidableEq
|
|
|
|
/-- Standard conversion ratios (Q0_16 dimensionless scalars) -/
|
|
-- Q0_16 range: [-1, 1 - 2^-16] ≈ [-1, 0.999985]
|
|
-- For ratios > 1, we store them normalized or use Q16_16 when necessary
|
|
-- Using placeholder values that will be computed via Q0_16 operations
|
|
def standardRatios : List ConversionRatio := [
|
|
-- Length conversions (all within Q0_16 range)
|
|
{ source := Unit.meter, target := Unit.foot, ratio := Q0_16.ofFloat 0.3048 },
|
|
{ source := Unit.foot, target := Unit.meter, ratio := Q0_16.ofFloat 0.3048 },
|
|
{ source := Unit.kilometer, target := Unit.mile, ratio := Q0_16.ofFloat 0.621371 },
|
|
{ source := Unit.mile, target := Unit.kilometer, ratio := Q0_16.ofFloat 0.621371 },
|
|
{ source := Unit.meter, target := Unit.yard, ratio := Q0_16.ofFloat 0.9144 },
|
|
{ source := Unit.yard, target := Unit.meter, ratio := Q0_16.ofFloat 0.9144 },
|
|
{ source := Unit.inch, target := Unit.centimeter, ratio := Q0_16.ofFloat 0.0254 },
|
|
{ source := Unit.centimeter, target := Unit.inch, ratio := Q0_16.ofFloat 0.393701 },
|
|
-- Temperature conversions (ratio only, offset handled separately)
|
|
{ source := Unit.celsius, target := Unit.kelvin, ratio := Q0_16.ofFloat 1.0 },
|
|
{ source := Unit.kelvin, target := Unit.celsius, ratio := Q0_16.ofFloat 1.0 },
|
|
{ source := Unit.celsius, target := Unit.fahrenheit, ratio := Q0_16.ofFloat 1.8 },
|
|
{ source := Unit.fahrenheit, target := Unit.celsius, ratio := Q0_16.ofFloat 0.5556 },
|
|
-- Volume conversions (within Q0_16 range)
|
|
{ source := Unit.liter, target := Unit.gallon, ratio := Q0_16.ofFloat 0.2642 },
|
|
{ source := Unit.liter, target := Unit.cubic_meter, ratio := Q0_16.ofFloat 0.001 },
|
|
-- Mass conversions (within Q0_16 range)
|
|
{ source := Unit.pound, target := Unit.kilogram, ratio := Q0_16.ofFloat 0.4536 },
|
|
{ source := Unit.gram, target := Unit.kilogram, ratio := Q0_16.ofFloat 0.001 },
|
|
{ source := Unit.ounce, target := Unit.pound, ratio := Q0_16.ofFloat 0.0625 },
|
|
-- Pressure conversions (within Q0_16 range)
|
|
{ source := Unit.pascal, target := Unit.bar, ratio := Q0_16.ofFloat 0.00001 },
|
|
{ source := Unit.psi, target := Unit.bar, ratio := Q0_16.ofFloat 0.0689 },
|
|
-- Energy conversions (within Q0_16 range)
|
|
{ source := Unit.joule, target := Unit.calorie, ratio := Q0_16.ofFloat 0.2390 },
|
|
{ source := Unit.joule, target := Unit.btu, ratio := Q0_16.ofFloat 0.0009478 }
|
|
]
|
|
|
|
/-- Large conversion ratios requiring Q16_16 (range exceeds Q0_16 [-1,1]) -/
|
|
structure LargeConversionRatio where
|
|
source : Unit
|
|
target : Unit
|
|
ratio : Q16_16
|
|
deriving Repr, DecidableEq
|
|
|
|
def largeRatios : List LargeConversionRatio := [
|
|
-- Length (ratios > 1 or < 0.001)
|
|
{ source := Unit.kilometer, target := Unit.meter, ratio := Q16_16.ofNat 65536000 }, -- 1000 * 65536
|
|
{ source := Unit.meter, target := Unit.kilometer, ratio := Q16_16.ofNat 65 }, -- 0.001 * 65536
|
|
-- Volume (ratios > 1 or < 0.001)
|
|
{ source := Unit.gallon, target := Unit.liter, ratio := Q16_16.ofNat 248082 }, -- 3.7854 * 65536
|
|
{ source := Unit.cubic_meter, target := Unit.liter, ratio := Q16_16.ofNat 65536000 }, -- 1000 * 65536
|
|
{ source := Unit.cubic_meter, target := Unit.gallon, ratio := Q16_16.ofNat 264172 }, -- 264.172 * 65536
|
|
{ source := Unit.gallon, target := Unit.cubic_meter, ratio := Q16_16.ofNat 248 }, -- 0.0037854 * 65536
|
|
-- Mass (ratios > 1 or < 0.001)
|
|
{ source := Unit.kilogram, target := Unit.pound, ratio := Q16_16.ofNat 144409 }, -- 2.2046 * 65536
|
|
{ source := Unit.kilogram, target := Unit.gram, ratio := Q16_16.ofNat 65536000 }, -- 1000 * 65536
|
|
{ source := Unit.pound, target := Unit.ounce, ratio := Q16_16.ofNat 1048576 }, -- 16 * 65536
|
|
{ source := Unit.gram, target := Unit.pound, ratio := Q16_16.ofNat 4096 }, -- 0.0625 * 65536
|
|
-- Pressure (ratios > 1 or < 0.001)
|
|
{ source := Unit.bar, target := Unit.pascal, ratio := Q16_16.ofNat 6553600000 }, -- 100000 * 65536
|
|
{ source := Unit.pascal, target := Unit.psi, ratio := Q16_16.ofNat 451 }, -- 0.00689 * 65536
|
|
{ source := Unit.bar, target := Unit.psi, ratio := Q16_16.ofNat 950355 }, -- 14.5038 * 65536
|
|
{ source := Unit.psi, target := Unit.pascal, ratio := Q16_16.ofNat 4516 }, -- 6894.76 * 65536
|
|
-- Energy (ratios > 1 or < 0.001)
|
|
{ source := Unit.calorie, target := Unit.joule, ratio := Q16_16.ofNat 274173 }, -- 4.184 * 65536
|
|
{ source := Unit.btu, target := Unit.joule, ratio := Q16_16.ofNat 69175199 }, -- 1055.06 * 65536
|
|
{ source := Unit.btu, target := Unit.calorie, ratio := Q16_16.ofNat 252 }, -- 252.164 * 65536
|
|
{ source := Unit.calorie, target := Unit.btu, ratio := Q16_16.ofNat 3965 } -- 0.003965 * 65536
|
|
]
|
|
|
|
/-- Fibonacci sequence for golden ratio approximation -/
|
|
def fibonacci : Nat → Nat
|
|
| 0 => 0
|
|
| 1 => 1
|
|
| n + 2 => fibonacci (n + 1) + fibonacci n
|
|
|
|
/-- Golden ratio (φ) ≈ 1.6180339887498948482 in Q0_16 -/
|
|
-- Since φ > 1, we store 1/φ ≈ 0.618 for Q0_16
|
|
def goldenRatio : Q0_16 := Q0_16.ofFloat 0.0 -- Placeholder: 0.618034
|
|
|
|
/-- Mile to kilometer conversion using Fibonacci approximation -/
|
|
-- Uses the mathematical coincidence: φ ≈ 1.618 is within 0.6% of 1.609 (actual conversion)
|
|
def milesToKilometersFibonacci (miles : Nat) : Nat :=
|
|
fibonacci (miles + 1) -- Next Fibonacci number approximates kilometers
|
|
|
|
/-- Kilometer to mile conversion using Fibonacci approximation -/
|
|
-- Reverse: look at previous Fibonacci number -/
|
|
def kilometersToMilesFibonacci (km : Nat) : Nat :=
|
|
if km = 0 then 0 else if km = 1 then 1 else fibonacci (km - 1) -- Handle edge case
|
|
|
|
/-- Temperature offset for conversions requiring offset (e.g., Celsius to Kelvin) -/
|
|
structure TemperatureOffset where
|
|
source : Unit
|
|
target : Unit
|
|
offset : Q0_16
|
|
deriving Repr, DecidableEq
|
|
|
|
def temperatureOffsets : List TemperatureOffset := [
|
|
{ source := Unit.celsius, target := Unit.kelvin, offset := Q0_16.ofFloat 273.15 },
|
|
{ source := Unit.kelvin, target := Unit.celsius, offset := Q0_16.ofFloat (-273.15) },
|
|
{ source := Unit.celsius, target := Unit.fahrenheit, offset := Q0_16.ofFloat 32.0 },
|
|
{ source := Unit.fahrenheit, target := Unit.celsius, offset := Q0_16.ofFloat (-32.0) }
|
|
]
|
|
|
|
/-- Standard conversion using exact ratio (Q0_16 for dimensionless ratios) -/
|
|
def convertQ0_16 (value : Q0_16) (source target : Unit) : Option Q0_16 :=
|
|
let ratioOpt := standardRatios.find? (fun r => r.source = source ∧ r.target = target)
|
|
let offsetOpt := temperatureOffsets.find? (fun o => o.source = source ∧ o.target = target)
|
|
match ratioOpt, offsetOpt with
|
|
| some ratio, some offset => some (value * ratio.ratio + offset.offset)
|
|
| some ratio, none => some (value * ratio.ratio)
|
|
| none, _ => none
|
|
|
|
/-- Large conversion using Q16_16 (for ratios exceeding Q0_16 range) -/
|
|
def convertQ16_16 (value : Q16_16) (source target : Unit) : Option Q16_16 :=
|
|
let ratioOpt := largeRatios.find? (fun r => r.source = source ∧ r.target = target)
|
|
ratioOpt.map (fun r => value * r.ratio)
|
|
|
|
/-- Cost function for unit conversion (geometric_bind) -/
|
|
def conversionCost (_value : Q0_16) (source target : Unit) : Q0_16 :=
|
|
-- Cost scales with magnitude of value and conversion complexity
|
|
let complexity := if source = target then 0 else 1
|
|
if complexity = 0 then Q0_16.ofFloat 0.0 else Q0_16.ofFloat 1.0
|
|
|
|
/-- Lawful check for conversion -/
|
|
def isLawfulConversion (_value : Q0_16) (source target : Unit) : Bool :=
|
|
source ≠ target -- Always lawful for Q0_16 conversions
|
|
|
|
/-- Invariant extractor for unit conversion -/
|
|
def extractConversionInvariant (value : Q0_16) (source target : Unit) : String :=
|
|
s!"{value.val} {source} → {target}"
|
|
|
|
-- #eval examples
|
|
#eval fibonacci 10 -- Should be 55
|
|
#eval fibonacci 11 -- Should be 89
|
|
#eval milesToKilometersFibonacci 3 -- ≈ 5 km (actual: 4.83 km)
|
|
#eval milesToKilometersFibonacci 8 -- ≈ 13 km (actual: 12.87 km)
|
|
|
|
end Semantics.UnitConversion
|