@@ -475,25 +475,136 @@ function format_jones(jp::String)
475475 replace (s, " q^" => " q^" )
476476end
477477
478- function knot_to_dict (record:: KnotRecord ; semantic= nothing )
478+ """
479+ format_alexander(poly) -> Union{String, Nothing}
480+
481+ Pretty-print a stored Alexander polynomial. The serialised form is
482+ `"exp:coeff,exp:coeff,..."` (produced by `_serialise_int_poly` in KnotTheoryExt).
483+ Renders using the variable `t`, e.g. `"-1:1,0:-3,1:1"` → `"t⁻¹ - 3 + t"`.
484+ Returns `nothing` for a nothing/missing input.
485+ """
486+ function format_alexander (poly:: Nothing )
487+ nothing
488+ end
489+
490+ function format_alexander (poly:: String )
491+ try
492+ terms = String[]
493+ for pair in split (poly, " ," )
494+ parts = split (pair, " :" )
495+ length (parts) == 2 || continue
496+ exp = parse (Int, parts[1 ])
497+ coef = parse (Int, parts[2 ])
498+ coef == 0 && continue
499+ base = exp == 0 ? " " :
500+ exp == 1 ? " t" :
501+ exp == - 1 ? " t⁻¹" :
502+ exp > 0 ? " t^$(exp) " : " t^($(exp) )"
503+ if coef == 1
504+ push! (terms, isempty (base) ? " 1" : base)
505+ elseif coef == - 1
506+ push! (terms, isempty (base) ? " -1" : " -$(base) " )
507+ else
508+ push! (terms, isempty (base) ? string (coef) : " $(coef)$(base) " )
509+ end
510+ end
511+ isempty (terms) && return " 0"
512+ # Join with sign-aware spacing
513+ result = terms[1 ]
514+ for t in terms[2 : end ]
515+ if startswith (t, " -" )
516+ result *= " - " * t[2 : end ]
517+ else
518+ result *= " + " * t
519+ end
520+ end
521+ result
522+ catch
523+ poly # fall back to raw string on any parse failure
524+ end
525+ end
526+
527+ """
528+ format_homfly(poly) -> Union{String, Nothing}
529+
530+ Render a HOMFLY-PT polynomial (Dict{Tuple{Int,Int},Int} keyed by (l-exp, m-exp))
531+ as a human-readable string, e.g. `"-l⁻¹m² + lm²"`.
532+ """
533+ function format_homfly (poly:: Nothing )
534+ nothing
535+ end
536+
537+ function format_homfly (poly:: Dict )
538+ isempty (poly) && return " 0"
539+ var_str = (exp:: Int , letter:: String ) ->
540+ exp == 0 ? " " :
541+ exp == 1 ? letter :
542+ exp == - 1 ? " $(letter) ⁻¹" :
543+ exp > 0 ? " $(letter) ^$(exp) " : " $(letter) ^($(exp) )"
544+ terms = String[]
545+ for (le, me) in sort (collect (keys (poly)))
546+ c = poly[(le, me)]
547+ c == 0 && continue
548+ lpart = var_str (le, " l" )
549+ mpart = var_str (me, " m" )
550+ mono = lpart * mpart
551+ isempty (mono) && (mono = " 1" )
552+ if c == 1
553+ push! (terms, mono)
554+ elseif c == - 1
555+ push! (terms, " -$(mono) " )
556+ else
557+ push! (terms, " $(c)$(mono) " )
558+ end
559+ end
560+ isempty (terms) && return " 0"
561+ result = terms[1 ]
562+ for t in terms[2 : end ]
563+ result *= startswith (t, " -" ) ? " - " * t[2 : end ] : " + " * t
564+ end
565+ result
566+ end
567+
568+ """
569+ compute_homfly(record) -> Union{String, Nothing}
570+
571+ Attempt on-demand HOMFLY-PT computation via KnotTheory.jl. Skips knots with
572+ more than 12 crossings (state-sum is exponential) and silently returns `nothing`
573+ on any error.
574+ """
575+ function compute_homfly (record:: KnotRecord )
576+ record. crossing_number > 12 && return nothing
577+ isnothing (record. pd_code) && return nothing
578+ try
579+ pd = to_planardiagram (record) # requires KnotTheory.jl loaded
580+ raw = KnotTheory. homfly_polynomial (pd)
581+ format_homfly (raw)
582+ catch
583+ nothing
584+ end
585+ end
586+
587+ function knot_to_dict (record:: KnotRecord ; semantic= nothing , homfly= nothing )
479588 Dict {String, Any} (
480- " id" => record. id,
481- " name" => record. name,
482- " gauss_code" => record. gauss_code. crossings,
483- " diagram_format" => record. diagram_format,
484- " crossing_number" => record. crossing_number,
485- " writhe" => record. writhe,
486- " genus" => record. genus,
589+ " id" => record. id,
590+ " name" => record. name,
591+ " gauss_code" => record. gauss_code. crossings,
592+ " diagram_format" => record. diagram_format,
593+ " crossing_number" => record. crossing_number,
594+ " writhe" => record. writhe,
595+ " genus" => record. genus,
487596 " seifert_circle_count" => record. seifert_circle_count,
488- " determinant" => record. determinant,
489- " signature" => record. signature,
597+ " determinant" => record. determinant,
598+ " signature" => record. signature,
490599 " alexander_polynomial" => record. alexander_polynomial,
491- " jones_polynomial" => record. jones_polynomial,
492- " jones_display" => format_jones (record. jones_polynomial),
493- " metadata" => record. metadata,
494- " semantic" => semantic,
495- " created_at" => string (record. created_at),
496- " updated_at" => string (record. updated_at),
600+ " alexander_display" => format_alexander (record. alexander_polynomial),
601+ " jones_polynomial" => record. jones_polynomial,
602+ " jones_display" => format_jones (record. jones_polynomial),
603+ " homfly_polynomial" => homfly,
604+ " metadata" => record. metadata,
605+ " semantic" => semantic,
606+ " created_at" => string (record. created_at),
607+ " updated_at" => string (record. updated_at),
497608 )
498609end
499610
610721function handle_knot_detail (db:: SkeinDB , sdb:: SemanticIndexDB , name:: String )
611722 record = fetch_knot (db, name)
612723 isnothing (record) && return error_response (" Knot '$name ' not found" ; status = 404 )
613- json_response (knot_to_dict (record; semantic = semantic_summary_by_name (sdb, record. name)))
724+ homfly = compute_homfly (record)
725+ json_response (knot_to_dict (record;
726+ semantic = semantic_summary_by_name (sdb, record. name),
727+ homfly = homfly))
614728end
615729
616730function handle_semantic_detail (db:: SkeinDB , sdb:: SemanticIndexDB , name:: String )
0 commit comments