import  numpy as  np
import  gdal
from  osgeo import  gdal
from  osgeo import  osr
from  osgeo import  ogr
from  osgeo.gdalconst  import  *
gdal.AllRegister ( )  # register all drivers 
gdal.UseExceptions ( ) 
 
'''http://m...content-available-to-author-only...l.com/blog/archive/2012/5/2/understanding-raster-basic-gis-concepts-and-the-python-gdal-library/''' 
 
############# 
# Functions # 
############# 
 
def  transform_utm_to_wgs84( easting,  northing,  zone) :
    utm_coordinate_system =  osr.SpatialReference ( ) 
 
    # Set geographic coordinate system to handle lat/lon 
    utm_coordinate_system.SetWellKnownGeogCS ( "WGS84" )  
    is_northern =  northing >  0     
    utm_coordinate_system.SetUTM ( zone,  is_northern) 
 
    # Clone ONLY the geographic coordinate system  
    wgs84_coordinate_system =  utm_coordinate_system.CloneGeogCS ( )  
 
    # create transform component 
    utm_to_wgs84_geo_transform =  osr.CoordinateTransformation ( utm_coordinate_system,  wgs84_coordinate_system)  # (, ) 
 
    # returns lon, lat, altitude 
    return  utm_to_wgs84_geo_transform.TransformPoint ( easting,  northing,  0 )  
 
class  WGS84Transform( object ) :
    # TODO decide whether to have init and/or call functions 
    #def transform_wgs84_to_utm(lon, lat) 
 
#    def __init__(self,lon,lat): 
#    self.lon = lon 
#	self.lat = lat 
 
    def  get_utm_zone( self , longitude) :
	return  ( int ( 1 +( longitude+180.0 ) /6.0 ) ) 
 
    def  is_lat_northern( self , latitude) :
	""" 
	Determines if given latitude is a northern for UTM 
	""" 
	if  ( latitude <  0.0 ) :
	    return  0 
	else :
	    return  1 
 
    def  wgs84_to_utm( self , lon, lat) :            
	    utm_coordinate_system =  osr.SpatialReference ( ) 
	    # Set geographic coordinate system to handle lat/lon   
	    utm_coordinate_system.SetWellKnownGeogCS ( "WGS84" )  
	    utm_coordinate_system.SetUTM ( self .get_utm_zone ( lon) ,  self .is_lat_northern ( lat) ) 
 
	    # Clone ONLY the geographic coordinate system   
	    wgs84_coordinate_system =  utm_coordinate_system.CloneGeogCS ( )  
 
	    # create transform component 
	    wgs84_to_utm_geo_transform =  osr.CoordinateTransformation ( wgs84_coordinate_system,  utm_coordinate_system)  # (, ) 
	    # returns easting, northing, altitude 
	    return  wgs84_to_utm_geo_transform.TransformPoint ( lon,  lat,  0 )  
 
def  get_iterable_extent( *args) :
    '''Returns list of minimum and maximum from lists/array input''' 
    iterable_extent =  list ( ) 
    for  iter_object in  args:
	iterable_extent.append ( min ( iter_object) ) 
	iterable_extent.append ( max ( iter_object) ) 
    return  iterable_extent 
 
 
def  get_raster_size( min_x,  min_y,  max_x,  max_y,  cell_width,  cell_height) :
    """Determine the number of rows/columns given the bounds of the point  
    data and the desired cell size""" 
 
    print  'raster min_x:' , min_x 
    print  'raster max_x:' , max_x 
    print  'raster min_y:' , min_y 
    print  'raster max_y:' , max_y 
    cols =  int ( ( max_x - min_x)  / cell_width) 
    rows =  int ( ( max_y - min_y)  / abs ( cell_height) ) 
    return  cols,  rows
 
 
def  lonlat_to_pixel( lon,  lat,  inverse_geo_transform) :
    """Translates the given lon, lat to the grid pixel coordinates 
    in data array (zero start)""" 
 
    wgs84_object =  WGS84Transform( ) 
    # transform to utm 
    utm_x,  utm_y,  utm_z =  wgs84_object.wgs84_to_utm ( lon,  lat) 
    print  'utm_x:' , utm_x
    print  'utm_y:' , utm_y
    print  'utm_z:' , utm_z
 
    # apply inverse geo tranform 
    pixel_x,  pixel_y =  gdal.ApplyGeoTransform ( inverse_geo_transform,  utm_x,  utm_y) 
    print  'pixel_x:' , pixel_x
    print  'pixel_y:' , pixel_y
    pixel_x =  int ( pixel_x)  - 1  # adjust to 0 start for array 
    pixel_y =  int ( pixel_y)  - 1  # adjust to 0 start for array 
 
 
    return  pixel_x,  abs ( pixel_y)  # y pixel is likly a negative value given geo_transform 
 
 
def  create_raster( lons, lats, values, filename= "test.tiff" , output_format= "GTiff" ) :      
    """lon/lat values in WGS84"""           
 
    # create empty raster 
    driver =  gdal.GetDriverByName ( output_format) 
    number_of_bands =  1 
    band_type =  gdal.GDT_Float32 
 
    x_rotation =  0 
    y_rotation =  0 
    cell_width_meters =  50.0 
    cell_height_meters =  50.0 
 
    # retrieve bounds for point data            
    min_lon,  max_lon,  min_lat,  max_lat =  get_iterable_extent( lons, lats)  
    print  'min_lon:' , min_lon 
    print  'max_lon:' , max_lon 
    print  'min_lat:' , min_lat 
    print  'max_lat:' , max_lat 
 
    # Set geographic coordinate system to handle lat/lon         
    srs =  osr.SpatialReference ( ) 
    srs.SetWellKnownGeogCS ( "WGS84" ) 
 
    # Set projected coordinate system  to handle meters          
    wgs84_obj =  WGS84Transform( ) 
    srs.SetUTM ( wgs84_obj.get_utm_zone ( min_lon) ,  wgs84_obj.is_lat_northern ( max_lat) )  
 
    # create transforms for point conversion 
    wgs84_coordinate_system =  srs.CloneGeogCS ( )  # clone only the geographic coordinate system    
    wgs84_to_utm_transform =  osr.CoordinateTransformation ( wgs84_coordinate_system,  srs) 
    utm_to_wgs84_transform =  osr.CoordinateTransformation ( srs,  wgs84_coordinate_system) 
 
    # convert to UTM 
    top_left_x,  top_left_y,  z =  wgs84_obj.wgs84_to_utm ( min_lon,  max_lat)     
    lower_right_x,  lower_right_y,  z =  wgs84_obj.wgs84_to_utm ( max_lon,  min_lat) 
    print  'top_left_x:' , top_left_x
    print  'top_left_y:' , top_left_y
    print  'lower_right_x:' , lower_right_x
    print  'lower_right_y:' , lower_right_y
 
    '''get_raster_size(min_x, min_y, max_x, max_y, cell_width, cell_height)''' 
    cols,  rows =  get_raster_size( top_left_x, lower_right_y, 
                                 lower_right_x, top_left_y, 
                                 cell_width_meters, cell_height_meters) 
    print  cols,  rows 
    dataset =  driver.Create ( filename,  cols,  rows,  number_of_bands,  band_type) 
 
    # GeoTransform parameters 
    # --> need to know the area that will be covered to define the geo tranform 
    # top left x, w-e pixel resolution, rotation, top left y, rotation, n-s pixel resolution 
    geo_transform =  [  top_left_x,  cell_width_meters,  x_rotation,  top_left_y,  y_rotation,  -cell_height_meters ]  # cell height must be negative (-) to apply image space to map 
    dataset.SetGeoTransform ( geo_transform) 
    dataset.SetProjection ( srs.ExportToWkt ( ) ) 
    inverse_geo_transform =  gdal.InvGeoTransform ( geo_transform) [ 1 ]  # for mapping lat/lon to pixel 
 
    # get the empty raster data array 
    band =  dataset.GetRasterBand ( 1 )  # 1 == band index value 
    data =  band.ReadAsArray ( 0 ,  0 ,  cols,  rows) .astype ( np.cfloat ) 
 
    # TODO check that lat/lon don't need to get meshed 
    # populate array values for output 
    for  lon,  lat,  value in  zip ( lons, lats, values) :
        # apply value to array 
	#TODO figure out why pixel_x is negative 
        pixel_x,  pixel_y =  lonlat_to_pixel( lon,  lat,  inverse_geo_transform) 
	print  pixel_x,  pixel_y,  data.shape 
        data[ pixel_x] [ pixel_y]  =  value
 
    # write the updated data array to file 
    band.WriteArray ( data,  0 ,  0 ) 
    band.SetNoDataValue ( NULL_VALUE) 
    band.FlushCache ( ) 
 
    # set dataset to None to "close" file 
    dataset =  None 
    # TODO have function return pixel values for interpolation 
    return  pixel_x,  pixel_y
 
################# 
# Main Function # 
################# 
 
if  __name__ ==  '__main__' :
	# example coordinates 
	lat =  [ 45.3 , 56.2 , 23.4 ] 
	lon =  [ 134.6 , 128.7 , 111.9 ] 
	val =  [ 3 , 6 , 2 ] 
 
	create_raster( lon, lat, val) 
aW1wb3J0IG51bXB5IGFzIG5wCmltcG9ydCBnZGFsCmZyb20gb3NnZW8gaW1wb3J0IGdkYWwKZnJvbSBvc2dlbyBpbXBvcnQgb3NyCmZyb20gb3NnZW8gaW1wb3J0IG9ncgpmcm9tIG9zZ2VvLmdkYWxjb25zdCBpbXBvcnQgKgpnZGFsLkFsbFJlZ2lzdGVyKCkgIyByZWdpc3RlciBhbGwgZHJpdmVycwpnZGFsLlVzZUV4Y2VwdGlvbnMoKQoKJycnaHR0cDovL20uLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLmwuY29tL2Jsb2cvYXJjaGl2ZS8yMDEyLzUvMi91bmRlcnN0YW5kaW5nLXJhc3Rlci1iYXNpYy1naXMtY29uY2VwdHMtYW5kLXRoZS1weXRob24tZ2RhbC1saWJyYXJ5LycnJwoKIyMjIyMjIyMjIyMjIwojIEZ1bmN0aW9ucyAjCiMjIyMjIyMjIyMjIyMKCmRlZiB0cmFuc2Zvcm1fdXRtX3RvX3dnczg0KGVhc3RpbmcsIG5vcnRoaW5nLCB6b25lKToKICAgIHV0bV9jb29yZGluYXRlX3N5c3RlbSA9IG9zci5TcGF0aWFsUmVmZXJlbmNlKCkKCiAgICAjIFNldCBnZW9ncmFwaGljIGNvb3JkaW5hdGUgc3lzdGVtIHRvIGhhbmRsZSBsYXQvbG9uCiAgICB1dG1fY29vcmRpbmF0ZV9zeXN0ZW0uU2V0V2VsbEtub3duR2VvZ0NTKCJXR1M4NCIpIAogICAgaXNfbm9ydGhlcm4gPSBub3J0aGluZyA+IDAgICAgCiAgICB1dG1fY29vcmRpbmF0ZV9zeXN0ZW0uU2V0VVRNKHpvbmUsIGlzX25vcnRoZXJuKQoKICAgICMgQ2xvbmUgT05MWSB0aGUgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIHN5c3RlbSAKICAgIHdnczg0X2Nvb3JkaW5hdGVfc3lzdGVtID0gdXRtX2Nvb3JkaW5hdGVfc3lzdGVtLkNsb25lR2VvZ0NTKCkgCiAgICAKICAgICMgY3JlYXRlIHRyYW5zZm9ybSBjb21wb25lbnQKICAgIHV0bV90b193Z3M4NF9nZW9fdHJhbnNmb3JtID0gb3NyLkNvb3JkaW5hdGVUcmFuc2Zvcm1hdGlvbih1dG1fY29vcmRpbmF0ZV9zeXN0ZW0sIHdnczg0X2Nvb3JkaW5hdGVfc3lzdGVtKSAjICgsICkKCiAgICAjIHJldHVybnMgbG9uLCBsYXQsIGFsdGl0dWRlCiAgICByZXR1cm4gdXRtX3RvX3dnczg0X2dlb190cmFuc2Zvcm0uVHJhbnNmb3JtUG9pbnQoZWFzdGluZywgbm9ydGhpbmcsIDApIAoKY2xhc3MgV0dTODRUcmFuc2Zvcm0ob2JqZWN0KToKICAgICMgVE9ETyBkZWNpZGUgd2hldGhlciB0byBoYXZlIGluaXQgYW5kL29yIGNhbGwgZnVuY3Rpb25zCiAgICAjZGVmIHRyYW5zZm9ybV93Z3M4NF90b191dG0obG9uLCBsYXQpCgojICAgIGRlZiBfX2luaXRfXyhzZWxmLGxvbixsYXQpOgojICAgIHNlbGYubG9uID0gbG9uCiMJc2VsZi5sYXQgPSBsYXQKCiAgICBkZWYgZ2V0X3V0bV96b25lKHNlbGYsbG9uZ2l0dWRlKToKCXJldHVybiAoaW50KDErKGxvbmdpdHVkZSsxODAuMCkvNi4wKSkKCSAgICAKICAgIGRlZiBpc19sYXRfbm9ydGhlcm4oc2VsZixsYXRpdHVkZSk6CgkiIiIKCURldGVybWluZXMgaWYgZ2l2ZW4gbGF0aXR1ZGUgaXMgYSBub3J0aGVybiBmb3IgVVRNCgkiIiIKCWlmIChsYXRpdHVkZSA8IDAuMCk6CgkgICAgcmV0dXJuIDAKCWVsc2U6CgkgICAgcmV0dXJuIDEKCiAgICBkZWYgd2dzODRfdG9fdXRtKHNlbGYsbG9uLGxhdCk6ICAgICAgICAgICAgCgkgICAgdXRtX2Nvb3JkaW5hdGVfc3lzdGVtID0gb3NyLlNwYXRpYWxSZWZlcmVuY2UoKQoJICAgICMgU2V0IGdlb2dyYXBoaWMgY29vcmRpbmF0ZSBzeXN0ZW0gdG8gaGFuZGxlIGxhdC9sb24gIAoJICAgIHV0bV9jb29yZGluYXRlX3N5c3RlbS5TZXRXZWxsS25vd25HZW9nQ1MoIldHUzg0IikgCgkgICAgdXRtX2Nvb3JkaW5hdGVfc3lzdGVtLlNldFVUTShzZWxmLmdldF91dG1fem9uZShsb24pLCBzZWxmLmlzX2xhdF9ub3J0aGVybihsYXQpKQoJICAgCgkgICAgIyBDbG9uZSBPTkxZIHRoZSBnZW9ncmFwaGljIGNvb3JkaW5hdGUgc3lzdGVtICAKCSAgICB3Z3M4NF9jb29yZGluYXRlX3N5c3RlbSA9IHV0bV9jb29yZGluYXRlX3N5c3RlbS5DbG9uZUdlb2dDUygpIAoJICAgIAoJICAgICMgY3JlYXRlIHRyYW5zZm9ybSBjb21wb25lbnQKCSAgICB3Z3M4NF90b191dG1fZ2VvX3RyYW5zZm9ybSA9IG9zci5Db29yZGluYXRlVHJhbnNmb3JtYXRpb24od2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0sIHV0bV9jb29yZGluYXRlX3N5c3RlbSkgIyAoLCApCgkgICAgIyByZXR1cm5zIGVhc3RpbmcsIG5vcnRoaW5nLCBhbHRpdHVkZQoJICAgIHJldHVybiB3Z3M4NF90b191dG1fZ2VvX3RyYW5zZm9ybS5UcmFuc2Zvcm1Qb2ludChsb24sIGxhdCwgMCkgCgpkZWYgZ2V0X2l0ZXJhYmxlX2V4dGVudCgqYXJncyk6CiAgICAnJydSZXR1cm5zIGxpc3Qgb2YgbWluaW11bSBhbmQgbWF4aW11bSBmcm9tIGxpc3RzL2FycmF5IGlucHV0JycnCiAgICBpdGVyYWJsZV9leHRlbnQgPSBsaXN0KCkKICAgIGZvciBpdGVyX29iamVjdCBpbiBhcmdzOgoJaXRlcmFibGVfZXh0ZW50LmFwcGVuZChtaW4oaXRlcl9vYmplY3QpKQoJaXRlcmFibGVfZXh0ZW50LmFwcGVuZChtYXgoaXRlcl9vYmplY3QpKQogICAgcmV0dXJuIGl0ZXJhYmxlX2V4dGVudCAKCgpkZWYgZ2V0X3Jhc3Rlcl9zaXplKG1pbl94LCBtaW5feSwgbWF4X3gsIG1heF95LCBjZWxsX3dpZHRoLCBjZWxsX2hlaWdodCk6CiAgICAiIiJEZXRlcm1pbmUgdGhlIG51bWJlciBvZiByb3dzL2NvbHVtbnMgZ2l2ZW4gdGhlIGJvdW5kcyBvZiB0aGUgcG9pbnQgCiAgICBkYXRhIGFuZCB0aGUgZGVzaXJlZCBjZWxsIHNpemUiIiIKCiAgICBwcmludCAncmFzdGVyIG1pbl94OicsbWluX3ggCiAgICBwcmludCAncmFzdGVyIG1heF94OicsbWF4X3ggCiAgICBwcmludCAncmFzdGVyIG1pbl95OicsbWluX3kgCiAgICBwcmludCAncmFzdGVyIG1heF95OicsbWF4X3kgCiAgICBjb2xzID0gaW50KChtYXhfeCAtIG1pbl94KSAvIGNlbGxfd2lkdGgpCiAgICByb3dzID0gaW50KChtYXhfeSAtIG1pbl95KSAvIGFicyhjZWxsX2hlaWdodCkpCiAgICByZXR1cm4gY29scywgcm93cwoKCmRlZiBsb25sYXRfdG9fcGl4ZWwobG9uLCBsYXQsIGludmVyc2VfZ2VvX3RyYW5zZm9ybSk6CiAgICAiIiJUcmFuc2xhdGVzIHRoZSBnaXZlbiBsb24sIGxhdCB0byB0aGUgZ3JpZCBwaXhlbCBjb29yZGluYXRlcwogICAgaW4gZGF0YSBhcnJheSAoemVybyBzdGFydCkiIiIKCiAgICB3Z3M4NF9vYmplY3QgPSBXR1M4NFRyYW5zZm9ybSgpCiAgICAjIHRyYW5zZm9ybSB0byB1dG0KICAgIHV0bV94LCB1dG1feSwgdXRtX3ogPSB3Z3M4NF9vYmplY3Qud2dzODRfdG9fdXRtKGxvbiwgbGF0KQogICAgcHJpbnQgJ3V0bV94OicsdXRtX3gKICAgIHByaW50ICd1dG1feTonLHV0bV95CiAgICBwcmludCAndXRtX3o6Jyx1dG1fegoKICAgICMgYXBwbHkgaW52ZXJzZSBnZW8gdHJhbmZvcm0KICAgIHBpeGVsX3gsIHBpeGVsX3kgPSBnZGFsLkFwcGx5R2VvVHJhbnNmb3JtKGludmVyc2VfZ2VvX3RyYW5zZm9ybSwgdXRtX3gsIHV0bV95KQogICAgcHJpbnQgJ3BpeGVsX3g6JyxwaXhlbF94CiAgICBwcmludCAncGl4ZWxfeTonLHBpeGVsX3kKICAgIHBpeGVsX3ggPSBpbnQocGl4ZWxfeCkgLSAxICMgYWRqdXN0IHRvIDAgc3RhcnQgZm9yIGFycmF5CiAgICBwaXhlbF95ID0gaW50KHBpeGVsX3kpIC0gMSAjIGFkanVzdCB0byAwIHN0YXJ0IGZvciBhcnJheQoKCiAgICByZXR1cm4gcGl4ZWxfeCwgYWJzKHBpeGVsX3kpICMgeSBwaXhlbCBpcyBsaWtseSBhIG5lZ2F0aXZlIHZhbHVlIGdpdmVuIGdlb190cmFuc2Zvcm0KCgpkZWYgY3JlYXRlX3Jhc3Rlcihsb25zLGxhdHMsdmFsdWVzLGZpbGVuYW1lPSJ0ZXN0LnRpZmYiLG91dHB1dF9mb3JtYXQ9IkdUaWZmIik6ICAgICAgCiAgICAiIiJsb24vbGF0IHZhbHVlcyBpbiBXR1M4NCIiIiAgICAgICAgICAKCiAgICAjIGNyZWF0ZSBlbXB0eSByYXN0ZXIKICAgIGRyaXZlciA9IGdkYWwuR2V0RHJpdmVyQnlOYW1lKG91dHB1dF9mb3JtYXQpCiAgICBudW1iZXJfb2ZfYmFuZHMgPSAxCiAgICBiYW5kX3R5cGUgPSBnZGFsLkdEVF9GbG9hdDMyCiAgIAogICAgeF9yb3RhdGlvbiA9IDAKICAgIHlfcm90YXRpb24gPSAwCiAgICBjZWxsX3dpZHRoX21ldGVycyA9IDUwLjAKICAgIGNlbGxfaGVpZ2h0X21ldGVycyA9IDUwLjAKCiAgICAjIHJldHJpZXZlIGJvdW5kcyBmb3IgcG9pbnQgZGF0YSAgICAgICAgICAgCiAgICBtaW5fbG9uLCBtYXhfbG9uLCBtaW5fbGF0LCBtYXhfbGF0ID0gZ2V0X2l0ZXJhYmxlX2V4dGVudChsb25zLGxhdHMpIAogICAgcHJpbnQgJ21pbl9sb246JyxtaW5fbG9uIAogICAgcHJpbnQgJ21heF9sb246JyxtYXhfbG9uIAogICAgcHJpbnQgJ21pbl9sYXQ6JyxtaW5fbGF0IAogICAgcHJpbnQgJ21heF9sYXQ6JyxtYXhfbGF0IAoKICAgICMgU2V0IGdlb2dyYXBoaWMgY29vcmRpbmF0ZSBzeXN0ZW0gdG8gaGFuZGxlIGxhdC9sb24gICAgICAgIAogICAgc3JzID0gb3NyLlNwYXRpYWxSZWZlcmVuY2UoKQogICAgc3JzLlNldFdlbGxLbm93bkdlb2dDUygiV0dTODQiKQoKICAgICMgU2V0IHByb2plY3RlZCBjb29yZGluYXRlIHN5c3RlbSAgdG8gaGFuZGxlIG1ldGVycyAgICAgICAgIAogICAgd2dzODRfb2JqID0gV0dTODRUcmFuc2Zvcm0oKQogICAgc3JzLlNldFVUTSh3Z3M4NF9vYmouZ2V0X3V0bV96b25lKG1pbl9sb24pLCB3Z3M4NF9vYmouaXNfbGF0X25vcnRoZXJuKG1heF9sYXQpKSAKCiAgICAjIGNyZWF0ZSB0cmFuc2Zvcm1zIGZvciBwb2ludCBjb252ZXJzaW9uCiAgICB3Z3M4NF9jb29yZGluYXRlX3N5c3RlbSA9IHNycy5DbG9uZUdlb2dDUygpICMgY2xvbmUgb25seSB0aGUgZ2VvZ3JhcGhpYyBjb29yZGluYXRlIHN5c3RlbSAgIAogICAgd2dzODRfdG9fdXRtX3RyYW5zZm9ybSA9IG9zci5Db29yZGluYXRlVHJhbnNmb3JtYXRpb24od2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0sIHNycykKICAgIHV0bV90b193Z3M4NF90cmFuc2Zvcm0gPSBvc3IuQ29vcmRpbmF0ZVRyYW5zZm9ybWF0aW9uKHNycywgd2dzODRfY29vcmRpbmF0ZV9zeXN0ZW0pCiAgIAogICAgIyBjb252ZXJ0IHRvIFVUTQogICAgdG9wX2xlZnRfeCwgdG9wX2xlZnRfeSwgeiA9IHdnczg0X29iai53Z3M4NF90b191dG0obWluX2xvbiwgbWF4X2xhdCkgICAgCiAgICBsb3dlcl9yaWdodF94LCBsb3dlcl9yaWdodF95LCB6ID0gd2dzODRfb2JqLndnczg0X3RvX3V0bShtYXhfbG9uLCBtaW5fbGF0KQogICAgcHJpbnQgJ3RvcF9sZWZ0X3g6Jyx0b3BfbGVmdF94CiAgICBwcmludCAndG9wX2xlZnRfeTonLHRvcF9sZWZ0X3kKICAgIHByaW50ICdsb3dlcl9yaWdodF94OicsbG93ZXJfcmlnaHRfeAogICAgcHJpbnQgJ2xvd2VyX3JpZ2h0X3k6Jyxsb3dlcl9yaWdodF95CiAgICAKICAgICcnJ2dldF9yYXN0ZXJfc2l6ZShtaW5feCwgbWluX3ksIG1heF94LCBtYXhfeSwgY2VsbF93aWR0aCwgY2VsbF9oZWlnaHQpJycnCiAgICBjb2xzLCByb3dzID0gZ2V0X3Jhc3Rlcl9zaXplKHRvcF9sZWZ0X3gsbG93ZXJfcmlnaHRfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG93ZXJfcmlnaHRfeCx0b3BfbGVmdF95LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsX3dpZHRoX21ldGVycyxjZWxsX2hlaWdodF9tZXRlcnMpCiAgICBwcmludCBjb2xzLCByb3dzIAogICAgZGF0YXNldCA9IGRyaXZlci5DcmVhdGUoZmlsZW5hbWUsIGNvbHMsIHJvd3MsIG51bWJlcl9vZl9iYW5kcywgYmFuZF90eXBlKQogICAKICAgICMgR2VvVHJhbnNmb3JtIHBhcmFtZXRlcnMKICAgICMgLS0+IG5lZWQgdG8ga25vdyB0aGUgYXJlYSB0aGF0IHdpbGwgYmUgY292ZXJlZCB0byBkZWZpbmUgdGhlIGdlbyB0cmFuZm9ybQogICAgIyB0b3AgbGVmdCB4LCB3LWUgcGl4ZWwgcmVzb2x1dGlvbiwgcm90YXRpb24sIHRvcCBsZWZ0IHksIHJvdGF0aW9uLCBuLXMgcGl4ZWwgcmVzb2x1dGlvbgogICAgZ2VvX3RyYW5zZm9ybSA9IFsgdG9wX2xlZnRfeCwgY2VsbF93aWR0aF9tZXRlcnMsIHhfcm90YXRpb24sIHRvcF9sZWZ0X3ksIHlfcm90YXRpb24sIC1jZWxsX2hlaWdodF9tZXRlcnMgXSAjIGNlbGwgaGVpZ2h0IG11c3QgYmUgbmVnYXRpdmUgKC0pIHRvIGFwcGx5IGltYWdlIHNwYWNlIHRvIG1hcAogICAgZGF0YXNldC5TZXRHZW9UcmFuc2Zvcm0oZ2VvX3RyYW5zZm9ybSkKICAgIGRhdGFzZXQuU2V0UHJvamVjdGlvbihzcnMuRXhwb3J0VG9Xa3QoKSkKICAgIGludmVyc2VfZ2VvX3RyYW5zZm9ybSA9IGdkYWwuSW52R2VvVHJhbnNmb3JtKGdlb190cmFuc2Zvcm0pWzFdICMgZm9yIG1hcHBpbmcgbGF0L2xvbiB0byBwaXhlbAoKICAgICMgZ2V0IHRoZSBlbXB0eSByYXN0ZXIgZGF0YSBhcnJheQogICAgYmFuZCA9IGRhdGFzZXQuR2V0UmFzdGVyQmFuZCgxKSAjIDEgPT0gYmFuZCBpbmRleCB2YWx1ZQogICAgZGF0YSA9IGJhbmQuUmVhZEFzQXJyYXkoMCwgMCwgY29scywgcm93cykuYXN0eXBlKG5wLmNmbG9hdCkKCiAgICAjIFRPRE8gY2hlY2sgdGhhdCBsYXQvbG9uIGRvbid0IG5lZWQgdG8gZ2V0IG1lc2hlZAogICAgIyBwb3B1bGF0ZSBhcnJheSB2YWx1ZXMgZm9yIG91dHB1dAogICAgZm9yIGxvbiwgbGF0LCB2YWx1ZSBpbiB6aXAobG9ucyxsYXRzLHZhbHVlcyk6CiAgICAgICAgIyBhcHBseSB2YWx1ZSB0byBhcnJheQoJI1RPRE8gZmlndXJlIG91dCB3aHkgcGl4ZWxfeCBpcyBuZWdhdGl2ZQogICAgICAgIHBpeGVsX3gsIHBpeGVsX3kgPSBsb25sYXRfdG9fcGl4ZWwobG9uLCBsYXQsIGludmVyc2VfZ2VvX3RyYW5zZm9ybSkKCXByaW50IHBpeGVsX3gsIHBpeGVsX3ksIGRhdGEuc2hhcGUKICAgICAgICBkYXRhW3BpeGVsX3hdW3BpeGVsX3ldID0gdmFsdWUKICAgICAgIAogICAgIyB3cml0ZSB0aGUgdXBkYXRlZCBkYXRhIGFycmF5IHRvIGZpbGUKICAgIGJhbmQuV3JpdGVBcnJheShkYXRhLCAwLCAwKQogICAgYmFuZC5TZXROb0RhdGFWYWx1ZShOVUxMX1ZBTFVFKQogICAgYmFuZC5GbHVzaENhY2hlKCkKICAgCiAgICAjIHNldCBkYXRhc2V0IHRvIE5vbmUgdG8gImNsb3NlIiBmaWxlCiAgICBkYXRhc2V0ID0gTm9uZQogICAgIyBUT0RPIGhhdmUgZnVuY3Rpb24gcmV0dXJuIHBpeGVsIHZhbHVlcyBmb3IgaW50ZXJwb2xhdGlvbgogICAgcmV0dXJuIHBpeGVsX3gsIHBpeGVsX3kKCiMjIyMjIyMjIyMjIyMjIyMjCiMgTWFpbiBGdW5jdGlvbiAjCiMjIyMjIyMjIyMjIyMjIyMjCgppZiBfX25hbWVfXyA9PSAnX19tYWluX18nOgoJIyBleGFtcGxlIGNvb3JkaW5hdGVzCglsYXQgPSBbNDUuMyw1Ni4yLDIzLjRdCglsb24gPSBbMTM0LjYsMTI4LjcsMTExLjldCgl2YWwgPSBbMyw2LDJdCgoJY3JlYXRlX3Jhc3Rlcihsb24sbGF0LHZhbCk=