module Polynomials module Analyzable AfterExtremaCurvatureMapping = { maximum: :right, minimum: :left } MinimumOrMaximum = {[1.0,-1.0] => :maximum,[-1.0,1.0] => :minimum} def strives_for roots = self.roots.map(&:x) return nil if roots.empty? [self.(roots.min - 1).sign, self.(roots.max + 1).sign].map { |d| d * Infinity } end def inflection_points self.derivative.local_extrema.map { |p| InflectionPoint.new(p.x,self.calculate(p.x)) }.to_set end def local_extrema derivative = self.derivative possible_extrema = derivative.roots.sort.map(&:x) return Set[] if possible_extrema.empty? samples = possible_extrema.sort.each_cons(2).map do |before,after| (before + after)/2 end samples.unshift possible_extrema.first - 1 samples.push possible_extrema.last + 1 possible_extrema.zip(samples.each_cons(2)).inject(Set[]) do |set,(pe,sample)| directions = sample.map { |x| derivative.(x).sign} kind_of_extremum = MinimumOrMaximum[directions] next set unless kind_of_extremum set << Extremum.new(pe,self.calculate(pe), kind_of_extremum) end end def curvature_behaviour hash = Hash.new {|h,k|h[k]=[]} dd = self.derivative.derivative return nil if !(strives_for = derivative.strives_for) || dd.(dd.roots.sort.last.to_f + 1) == 0 ([[-Infinity,strives_for[0]]] + derivative.local_extrema.sort + [[Infinity,strives_for[-1]]]).each_cons(2).chunk do |b,_| if b.is_a? Point AfterExtremaCurvatureMapping[b.kind_of_extremum] else b.last < 0 ? :left : :right end end.with_index.inject(hash) do |hash,((curvature,values),i)| hash.tap { |h| h[curvature].concat values.each {|a| a.map! { |p| (p.is_a? Point) ? p.x : p.first }}} end end end end