"""Independent rational arithmetic and deliberate bad-input fixtures; no PDE/ODE."""
from copy import deepcopy
from fractions import Fraction
import importlib.util
import json
from pathlib import Path
import sys
MATERIALS = Path(__file__).resolve().parent
spec = importlib.util.spec_from_file_location('perseus_replay', MATERIALS/'reproduce-perseus.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)


def main():
    data = json.loads((MATERIALS/'perseus-input.json').read_text())
    result = module.calculate(data)
    # Hand-selected inner pV values and year conversion represented as exact rationals.
    energy_j = Fraction(4) * (Fraction(37,10)+Fraction(16,10)) * 10**51
    power_w = (Fraction(4)*Fraction(37,10)/Fraction(16,10)
        + Fraction(4)*Fraction(16,10)/Fraction(11,10)) * Fraction(10**51,10**7*31557600)
    ratio = power_w / (533 * 10**35)
    rows=[]
    def check(name, valid):
        rows.append({'name':name,'pass':bool(valid)})
        if not valid:
            raise AssertionError(name)
    check('Independent rational enthalpy2.12e52J', Fraction(result['innerPairEnthalpyJ']) == energy_j)
    check('Independent rational buoyancy power relative1e-45', abs(Fraction(result['ageComparisons'][0]['pairConditionalPowerW'])/power_w-1) < Fraction(1,10**45))
    check('Independent rational heating comparison relative1e-45', abs(Fraction(result['ageComparisons'][0]['powerOverRequiredHeating'])/ratio-1) < Fraction(1,10**45))
    check('Fixed-age asymmetric sensitivity straddles unity', Fraction(result['ageComparisons'][0]['fixedAgeEndpointSensitivityRatio'][0]) < 1 < Fraction(result['ageComparisons'][0]['fixedAgeEndpointSensitivityRatio'][1]))
    check('Three age prescriptions change the physical conclusion', Fraction(result['ageComparisons'][1]['powerOverRequiredHeating'])>1 and Fraction(result['ageComparisons'][2]['powerOverRequiredHeating'])<1)
    check('Published aggregate is retained as different estimator', result['publishedAggregateEstimatorReproduced'] is False and Fraction(result['tutorialToPublishedAggregatePowerRatio'])>3)
    mutations=[
        ('UNIT_MISMATCH',lambda item:item['units'].__setitem__('luminosity','Msun/yr')),
        ('NONPOSITIVE_AGE',lambda item:item['cavities'][0].__setitem__('buoyancyAge','0')),
        ('NONPOSITIVE_CAVITY_PV',lambda item:item['cavities'][0].__setitem__('pVMinus','4')),
        ('NONPOSITIVE_HEATING_REQUIREMENT',lambda item:item['aperture'].__setitem__('Lcool','555')),
        ('DO_NOT_MIX_CAVITY_GENERATIONS',lambda item:item['tutorial'].__setitem__('selectedCavityIds',['inner-row1','outer-row3'])),
        ('APERTURE_FOOTNOTE_MISSING',lambda item:item['aperture'].__setitem__('kind','full-cooling-radius')),
        ('RELATIVISTIC_ENTHALPY_FACTOR',lambda item:item['tutorial'].__setitem__('enthalpyFactor','1')),
        ('SOURCE_VERSION',lambda item:item['source'].__setitem__('arxiv','unversioned')),
    ]
    for expected,mutate in mutations:
        bad=deepcopy(data);mutate(bad)
        try:
            module.calculate(bad)
        except ValueError as error:
            check('Bad input rejected '+expected,str(error)==expected)
        else:
            raise AssertionError('Bad input accepted '+expected)
    receipt={'status':'PASS_PERSEUS_RATIONAL_AND_BAD_INPUT_FIXTURES','checks':rows,'newPDEOrODECalls':0,'actualPython':sys.version}
    print(json.dumps(receipt,indent=2))



if __name__=='__main__':
    main()
