 #
 # This file is part of the Anotatornia suite.
 # 
 # Copyright © 2007, 2008, 2009, 2010 by Instytut Podstaw Informatyki
 # Polskiej Akademii Nauk (IPI PAN; Institute of Computer Science, Polish
 # Academy of Sciences; cf. www.ipipan.waw.pl).  All rights reserved.
 # 
 # This file may be distributed and/or modified under the terms of the
 # GNU General Public License version 3 as published by the Free Software
 # Foundation and appearing in the file COPYING included in the packaging
 # of this file.  (See http://www.gnu.org/licenses/translations.html for
 # unofficial translations.)
 # 
 # A commercial license is available from IPI PAN (contact
 # Michal.Ciesiolka.waw.pl or ipi.waw.pl for more
 # information).  Licensees holding a valid commercial license from IPI
 # PAN may use this file in accordance with that license.
 # 
 # This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
 # THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 # PURPOSE.
 #

module Ramkowanie
# moduł, który włączam do FrazaAnot i do Ramka.

CASES = /(nom|gen|dat|acc|inst|loc|voc)/

UNDEF = 'UNDEF'


@@srt = nil

def self.srt
  unless @@srt
    @@srt = Proc.new {|a128, b128| 
      x134 = (a128[0] <=> b128[0])
      x134= (a128[1] <=> b128[1] ) if x134 == 0 
      x134}
  end
  @@srt
end # of #{self.srt}.

end # of #{module Ramkowanie}.


class Ihash < Hash
  def self.new( a_hash134={} )
    h130 = Hash.new {
      # Proca, która będzie wykonywana dla domyślnego eltu hasza.
      # Służy znalezieniu odp. wartości w haszu indeksowanym przedziałami lub tablicami.
      |hash129, key129|
      found134 = false
      v130 = hash129.each{|k129, val129| 
        if k129.respond_to?( :include? ) and k129.include?( key129 )
          found134 = true
          break( val129 )
        end}
      v130  if found134
    }
    h130.merge!( a_hash134 )
    return h130
  end# of #{self.new},

end # of #{class Ihash}




class Array

  def proj( axis131 )
    self.collect{ |x131| x131[axis131] }
  end

  def dx
    self.proj( 0 )
  end

  def dy
    self.proj( 1 )
  end

  def dz
    self.proj( 2 )
  end

  def dt
    self.proj( 3 )
  end

  
  def includetext? ( tx132 )
    # zadziała dla tablicy stringów, i zwróci false, jeśli żaden ze stringów 
    # nie zawiera/regexp tx, albo tablicę indeksów  dopasowania.
    incl133 = Array.new
    if tx132.kind_of?( Regexp )
      self.each_index{ |i132|
        incl133 << i132 if (self[i132] =~ tx132) }
    elsif tx132.kind_of?( String )
      self.each_index{ |i133|
        incl133 << i133 if self[i133].include?( tx132 )  }
    end
    incl133 = false unless incl133[ 0 ]
    return incl133
  end # of includetext?


  def to_arghash( ohash135 = {} )
    # zakładamy, że jest to tablica [ tag, sensy-opis, ew. vp_typ lub typ_sie ]
    h135=self[0].to_arghash( ohash135 )
    s135=self[1].split.collect{|s136| Sensy.opis2ozn(s136) }
    
    h135[:sens] = s135 if s135[0]
    
    if h135[:syncat] == FrazaTyp.downhash['sie'] and self[2]
      h135[:oblig] = ( self[2] != FrazaAnot::TYP_SIE[3][1] )
    end
    h135
  end# of to_arghash

  def niedopasowania( arr137 )
    #    zwraca tablicę [[indeksy tych, które są w 0 a nie w 1], [indeksy tych, które są w 1 i nie w 0 ]]
    arr0_137 = self
    arr0_d = arr0_137.dup
    arr1_d = arr137.dup
    # odhaczamy kolejne tagi z jednej w drugiej i odwrotnie
    arr0_137.each{ |elt137|
      i137 = arr1_d.index( elt137 )
      arr1_d.delete_at(i137) if i137}# delete at, a nie po prostu delete, żeby usunąć tylko jeden na raz
    arr137.each{ |elt138|
      i138 = arr0_d.index( elt138 )
      arr0_d.delete_at(i138) if i138}
    wy0_138, wy1_138 = [], []
    dup2 = arr0_137.dup
    arr0_d.each{ |elt139|
      # znajdujemy ostatnie wystąpienie tagu niesymetrycznego
      # i dodajemy id tej elipsy do tablicy, i odhaczamy ten tag w tablicy tagów,
      # żeby go nie znaleźć po raz drugi.
      i139 = dup2.rindex( elt139 )
      wy0_138 << i139
      dup2[i139] = nil}
    dup2 = arr137.dup
    arr1_d.each{ |elt140|
      # znajdujemy ostatnie wystąpienie tagu niesymetrycznego
      # i dodajemy id tej elipsy do tablicy, i odhaczamy ten tag w tablicy tagów,
      # żeby go nie znaleźć po raz drugi.
      i140 = dup2.rindex( elt140 )
      wy1_138 << i140
      dup2[i140] = nil}
    return [wy0_138, wy1_138]
  end# of #{niedopasowania}.
  


  def to_xml_attr_alpha # używana w~eksporcie danych do \ac{XML}\dy a.
    # zwrócimy hasz, który Builder zapisze jako ciąg atrybutów w~kolejności alfabetycznej.
    # zakładamy, że jesteśmy tablicą tablic dwuelementowych, której dx jest wektorem atrybutów, a dy wektorem wartości (jesteśmy wynikiem #{aHash.to_a})
    s140 = self.collect{ |kv140| kv140.collect{ |x140| x140.to_s } }.sort
    s0_140 = s140.delete_at(0)
    tail140=s140.collect{ |kv141| "#{kv141[0]}=\"#{kv141[1]}" }.join('" ') # zwróćcie uwagę, że zamykający cudzysłów dołącza #{join} — ostatnie pozostanie nie domknięte, i~o~to właśnie chodzi.

    return { s0_140[0].intern => [s0_140[1], if tail140.length>0 : tail140 end ].compact.join('" ').intern # ten #intern  sprawia, że cudzysłowy (ani nic innego) nie zostaną ucieknione.
    }
  end



end# of #{class Array}.


class Hash
  
  def to_argarr( trim=nil )
    arg = self
    if trim : tag0= ''
    else
      if arg[:oblig] : tag0='fw:' 
      else tag0 = 'fl:'
      end
    end
    
    if arg[:syncat]
      typ=FrazaTyp.find( arg[:syncat] ).typ_symbol.downcase
    else
      # dwie ramki nie mają pola syncat, i nie wiem dlaczego,
      # a nie mam czasu drążyć (trochę podrążyłem — plik test.rb)
      if arg[:prep] : typ = 'prepnp'
      else typ = 'np'
      end
    end
    
    tag0 += typ 
    tag0 += ':' unless typ =~ /(advp|sie)/
    
    case typ
            
    when 'advp'
      
    when /^(adjp|np|nump)$/ 
      tag0 += arg[:case]
      
    when /^(prepadjp|prepnp|prepnump)$/
      tag0 += arg[:prep] +':'+ arg[:case]
      
    when 'sie' # Ela chce wyróżniać „się” bezosobowe jako frazę luźną.
      #        tag0 += 'sie'
    else
      if typ==nil :   tag0=':???'
      else      raise "nie rozpoznany typ frazy: #{typ}"
      end
    end # of case
    
    #     tag0+= ':' 
    [ tag0, 
      if arg[:sens] 
        arg[:sens].collect{|s| Sensy.ozn2opis( s )}.join(" ")
        # 2008/07/18 Ela chce sensy pełnym słowem.
      else ''
      end ]
  end # of to_argarr

  
  def to_xml_attr_alpha
    # zwrócimy się w postaci Builderowego ciągu atrybutów posortowanych alfabetycznie.
    self.to_a.to_xml_attr_alpha
  end

end # of class Hash


class String
  
  def sens_pusty?
    self == '' or self == Ramkowanie::UNDEF or self == nil
  end

  def to_arghash( ohash={} )
    #    to może być  :zostawtyp => true lub :oblig => true/false
    # przetwarza tag frazy (argumentu ramki) w hasz do zapisania w ramka.rbo
    tag=self

    zmientyp = (not ohash[:zostawtyp] )

    ts=tag.split(':')
    h=Hash.new
    if ts[0] =~ /^f([a-z])$/ # jeśli pasuje do fw lub fl, to oblig = true <-> fw
      h[:oblig] = ($1 == 'w')
      ts.delete_at( 0 )
    else
      h[:oblig] =true
    end

    h[:oblig] = ohash[:oblig] if ohash.has_key?( :oblig )

    case ts[0]
        
    when 'advp$'
      
    when /^(adjp|np|nump)$/ 
      h[:case] = ts[1]
      ts[0]='np' if zmientyp
      
    when /^(prepadjp|prepnp|prepnump)$/
      h[:prep],  h[:case] = ts[1], ts[2]
      ts[0]='prepnp' if ts[0]=='prepnump' and zmientyp 

    when 'sie' # Ela chce wyróżniać „się” bezosobowe jako frazę luźną.
      #        tag0 += 'sie'
    end# of case

    h[:syncat] = FrazaTyp.downhash[ ts[0] ] 

    # sprawdzamy, czy tag (z zapewne zamienionym typem) jest na liście elips
    if zmientyp
#      raise "tag niepoprawny!!!" unless Elipsa.lista_fraz.include?( ts.join(':') )
    end
    h
  end# of to_arghash


  def trimtyparg
    self =~ /^(f[a-z]:)?(.*)$/
    $2.to_s 
  end


  def to_bool
    if (sds = self.downcase.strip) == 'true'  or sds == 't' or self.to_i != 0  : true
    else false
    end
  end # of to_bool

  @@depolon = { 
    'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ż' => 'z', 'ź' => 'z',
    'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'E', 'Ł' => 'L', 'Ń' => 'N', 'Ó' => 'O', 'Ś' => 'S', 'Ż' => 'Z', 'Ź' => 'Z' 
  }
  
  def odpolszcz
    self.gsub(/([ąćęłńóśżź])/) {|match192| @@depolon[ match192 ] }
  end
  

  def nkjp_escape
    # 2009/9/10 po konsultacji z~MW co będzie a~co nie będzie poprawnym \ac{XML}\dy em, co zgadza się z~empirią niewyeskejpowanego cudzysłowu, eskejpuję trzy z~czterech \ac{HTML}\dy owych. 
    self.gsub(/&/, "&amp;"). ##gsub(/\"/, "&quot;").
      gsub(/>/, "&gt;").gsub(/</, "&lt;")
  end


end # of #{class String}


class ActiveRecord::Base
  def self.pragmas_for_update
    logger "@@@ pragmas for update #{Time.now.to_s(:db)}"
    self.connection.execute " pragma locking_mode = exclusive; "
    self.connection.execute " pragma synchronous=off; "
  end

  def self.pragmas_normal
    self.connection.execute " pragma locking_mode = normal; "  
    self.connection.execute " pragma synchronous=normal; "
  end
  
  def self.max_created_at( conditions )
    self.maximum( :created_at, :conditions => conditions ) || '1969-06-29'.to_time
  end

  def self.unnull_akat_bliźniaki
    self.connection.execute "update akapit_transzy set blizniaczy_id =
(select akapit_transzy_id from akapit_transzy  at1
where at1.akapit_id= akapit_transzy.akapit_id 
and at1.akapit_transzy_id <> akapit_transzy.akapit_transzy_id)
where blizniaczy_id is null"
    # poniższe doprowadziło do tego, że przy kolejnej dolewce 30 akatów nie miało bliźniaka.
    ## and akapit_id between #{akapit_od} and #{akapit_do}
  end

end # of #{ActiveRecord::Base}


class Range

  def  min
    if self.begin <= self.end
      self.begin
    end
  end

  def max
    if self.begin <= self.end
      self.end
    end
  end


  def &( drugie )
    # oczywiście nie możemy skonwertować do #{Array}: zakresy mogą być gigantyczne.
    if self.include?( drugie.min ) or self.include?( drugie.max ) or
        drugie.include?( self.min ) or drugie.include?( self.max )
      krańce = [self.max, self.min,  drugie.min, drugie.max].sort
      (krańce[1])..(krańce[2])
    else
      nil
    end
  end # of #&


end # of #{class Range}
  
class Zmienna4RadioButton < Object
  attr_accessor :wartość
end


module XpointerCreator
  # jego metodę używam w #NowaSegmentacja i #{SgVariant}.
  
  def create_xpointers( orths_list, tokens_list, is_list=nil )
    # tworzymy tablicę xpointerów odpowiadających orthom wedle xpointerów podanych tokenów. Nie sprawdzamy, czy dwie listy wejściowe są kompatybilne: w #{NowaSegmentacja} zrobiliśmy to wcześniej, w #{SgVariant} zakładamy, że wariant dodany został dodany poprawnie (z dokładnością do xpointerów).
    # #dii jest tablicą tokenów. Bierzemy ich xpointery, które mówią, od którego znaku się token zaczyna i na którym kończy, i~przeliczamy xpointery dla dodawanych tokenów.

    if is_list
      orths_is_list = is_list
    else
      orths_is_list = tokens_list.collect { |t144| t144.orth }
    end

##    puts tokens_list.collect { |t144| t144.id }.join( ' ')
 
    md143 = nil

    xps144 = tokens_list.collect { |t144| 
      logger.info "@@@@ t.xpointer of #{t144.id}: #{t144.xpointer.inspect}"
      md144 = /^(.*),(\d+),(\d+)\)$/.match( t144.xpointer )
                                                  logger.info "@@@@ #{md144.inspect}"

      [ md144[ 1 ] + ',', # dodajemy przecinek, bo xpointery mają postać „…ab,nr,nr”.
        md144[ 2 ].to_i, md144[3].to_i ]
    }
#   #xps jest teraz tablicą trójek,  z~których każda zawiera początek xpointera, czyli |string-range(txt_19.1-ab|, nr znaku od, liczbę znaków.

    süßtaffel = []
    orths_is_list.each_index { |i144|
      # teraz tablicę orthów #seg_is obrabiamy w~ ten sposób, że każdy orth dzielimy na epsilonie, czyli zamieniamy w~tablicę pojedynczych znaków
      orths_is_list[i144].split("").
      # następnie przebiegamy indeksy tej tablicy i~dla każdego z~nich wpisujemy do #süßtaffel wartość odpowiedniego xpointera „od znaku”.
      each_index {|j144|
        süßtaffel << ( xps144[i144][1] + j144 )
      }
    }
    # Tym sposobem #süßtaffel zawiera teraz numery znaków, odpowiadające kolejnym  znakom orthów #{seg_is}.
    # Zauważmy, że nie muszą to być liczby ściśle kolejne: zdarzają się przeskoki tam, gdzie w~tekście jest spacja. Są to jednak z~pewnością liczby parami \emph{różne}, a~nawet — uporządkowane ściśle rosnąco.

    sxps144 = [] # „Should-be XPointerS”
    orths_list.each_index { |i145|
      # Teraz obramiamy orthy #{seg_should_be}:
      # pobieramy z~#süßtaffel do #untertaffel tyle znaków, ile długość kolejnego ortha…
      ojl =  orths_list[ i145 ].jlength
      untertaffel = süßtaffel[ 0, ojl]
      # i~usuwamy je z~#süßtaffel:
      süßtaffel = süßtaffel[ ojl, [süßtaffel.size - ojl, 1].max ]
      
      # a~następnie dopisujemy do tablicy nowych (szudbicznych) xpointerów nowy xpointer

      sxps144 << ( xps144[ [xps144.size-1, i145].min ][0] + # to jest początek $i$-tego lub przedostatniego xpointera oryginalnego \gobble(
                   "#{untertaffel[0]},#{untertaffel[-1]-untertaffel[0]+1})" # a to wartość „od znaku”, liczba znaków (#{+1} dodane 2010/2/24 po zgłoszeniu błędu przez analizujących wyjście). Domykamy nawias xpointera.
                )
    }
    sxps144 # zwracamy „Should-be XPointerS”

  end # of #{create_xpointers}


end # of module #{XpointerCreator}


### Local Variables: 
### mode: ruby
### End:
