def randrange(self, start, stop=None, step=1, int=int, default=None): istart = int(start) if istart != start: raise ValueError, 'non-integer arg 1 for randrange()' if stop is default: if istart > 0: return int(self.random() * istart) raise ValueError, 'empty range for randrange()' istop = int(stop) if istop != stop: raise ValueError, 'non-integer stop for randrange()' if step == 1 and istart < istop: try: return istart + int(self.random() * (istop - istart)) except OverflowError: return int(istart + _floor(self.random() * (istop - istart))) if step == 1: raise ValueError, 'empty range for randrange()' istep = int(step) if istep != step: raise ValueError, 'non-integer step for randrange()' if istep > 0: n = (istop - istart + istep - 1) / istep else: if istep < 0: n = (istop - istart + istep + 1) / istep else: raise ValueError, 'zero step for randrange()' if n <= 0: raise ValueError, 'empty range for randrange()' return istart + istep * int(self.random() * n)
def get_dims(num): sq = _sqrt(num) cols = _ceil(sq) rows = _floor(sq) if cols * rows < num: rows = rows + 1 return (rows, cols)
def floor(x, options=None): """Return the floor of *x*. If *options* is set, return the largest integer or float from *options* that is less than or equal to *x*. Args: x (int or float): Number to be tested. options (iterable): Optional iterable of arbitrary numbers (ints or floats). >>> VALID_CABLE_CSA = [1.5, 2.5, 4, 6, 10, 25, 35, 50] >>> floor(3.5, options=VALID_CABLE_CSA) 2.5 >>> floor(2.5, options=VALID_CABLE_CSA) 2.5 """ if options is None: return _floor(x) options = sorted(options) i = bisect.bisect_right(options, x) if not i: raise ValueError("no floor options less than or equal to: %r" % x) return options[i - 1]
def round(space, number, ndigits=0): """round(number[, ndigits]) -> floating point number Round a number to a given precision in decimal digits (default 0 digits). This always returns a floating point number. Precision may be negative.""" # Algortithm copied directly from CPython f = 1.0 if ndigits < 0: i = -ndigits else: i = ndigits while i > 0: f = f * 10.0 i -= 1 if ndigits < 0: number /= f else: number *= f if number >= 0.0: number = _floor(number + 0.5) else: number = _ceil(number - 0.5) if ndigits < 0: number *= f else: number /= f return space.wrap(number)
def round(space, number, ndigits=0): """round(number[, ndigits]) -> floating point number Round a number to a given precision in decimal digits (default 0 digits). This always returns a floating point number. Precision may be negative.""" # Algortithm copied directly from CPython f = 1.0 if ndigits < 0: i = -ndigits else: i = ndigits while i > 0: f = f*10.0 i -= 1 if ndigits < 0: number /= f else: number *= f if number >= 0.0: number = _floor(number + 0.5) else: number = _ceil(number - 0.5) if ndigits < 0: number *= f else: number /= f return space.wrap(number)
def randrange(self, start, stop=None, step=1, int=int, default=None): istart = int(start) if (istart != start): raise ValueError, 'non-integer arg 1 for randrange()' if (stop is default): if (istart > 0): return int((self.random() * istart)) raise ValueError, 'empty range for randrange()' istop = int(stop) if (istop != stop): raise ValueError, 'non-integer stop for randrange()' if ((step == 1) and (istart < istop)): try: return (istart + int((self.random() * (istop - istart)))) except OverflowError: return int((istart + _floor( (self.random() * (istop - istart))))) if (step == 1): raise ValueError, 'empty range for randrange()' istep = int(step) if (istep != step): raise ValueError, 'non-integer step for randrange()' if (istep > 0): n = ((((istop - istart) + istep) - 1) / istep) elif (istep < 0): n = ((((istop - istart) + istep) + 1) / istep) else: raise ValueError, 'zero step for randrange()' if (n <= 0): raise ValueError, 'empty range for randrange()' return (istart + (istep * int((self.random() * n))))
def is_prime(integer): """Returns True if ``integer`` is a prime, otherwise False.""" assert integer < primes[-1] ** 2 integer = -integer if integer < 0 else integer limit = _floor(_sqrt(integer)) + 1 for i in _takewhile(lambda elem: elem < limit, primes): if integer % i == 0: return False return integer > 1
def _randbelow_without_getrandbits(self, n, maxsize=1<<BPF): """Return a random int in the range [0,n). Defined for n > 0. The implementation does not use getrandbits, but only random. """ random = self.random if n >= maxsize: _warn("Underlying random() generator does not supply \n" "enough bits to choose from a population range this large.\n" "To remove the range limitation, add a getrandbits() method.") return _floor(random() * n) rem = maxsize % n limit = (maxsize - rem) / maxsize # int(limit * maxsize) % n == 0 r = random() while r >= limit: r = random() return _floor(r * maxsize) % n
def set_bg(self, bg): """ Sets the background color. :type bg: list(int * 3) :param bg: background color """ glClearColor(*[_floor(color % 256) / 255 for color in bg], 1) self.bg = bg
def updIB(self): self.IB.clear() for p in self.points: limit = 0 for elem in reversed(self.rank_list[p]): if limit < self.q and elem[1] in self.points: self.IB.add(elem[1]) limit += 1 self.ib = _floor(_sqrt(len(self.IB))).__int__()
def _number_profile(value, precision): ''' returns: string of significant digits 10s exponent to get the dot to the proper location in the significant digits bool that's true if value is less than zero else false created by William Rusnack github.com/BebeSparkelSparkel linkedin.com/in/williamrusnack/ [email protected] contributions by Thomas Hladish github.com/tjhladish Issue: github.com/BebeSparkelSparkel/to-precision/issues/5 ''' value = float(value) is_neg = value < 0 value = abs(value) if value == 0: sig_digits = '0' * precision power = -(1 - precision) else: if _math.isnan(value): return _math.nan power = -1 * _floor(_log10(value)) + precision - 1 # issue soved by Thomas Haladish # github.com/BebeSparkelSparkel/to-precision/issues/5 value_power = value * 10.0**power if value < 1 and \ _floor(_log10(int(round(value_power)))) > \ _floor(_log10(int(value_power))): power -= 1 sig_digits = str(int(round( value * 10.0**power))) # cannot use value_power because power is changed return sig_digits, int(-power), is_neg
def updOB(self): self.OB.clear() outer_space = self.world.difference(self.points) for p in self.points: limit = 0 for elem in self.rank_list[ p]: ##se ci sono doppioni nel training set, qui da' KeyError!!! if limit < self.k and elem[1] in outer_space: self.OB.add(elem[1]) limit += 1 self.ob = _floor(_sqrt(len(self.OB))).__int__()
def updOB(self): """updates the outer border of the cluster. for each point inside the cluster scans the related rank list for external points""" self.OB.clear() outer_space = welt[self.klasse].difference(self.points) for p in self.points: limit = 0 for elem in rank_list[p]: if limit < k and elem[1] in outer_space: self.OB.add(elem[1]) limit += 1 self.ob = _floor(_sqrt(len(self.OB))).__int__()
def randrange(self, start, stop=None, step=1, int=int, default=None): """Choose a random item from range(start, stop[, step]). This fixes the problem with randint() which includes the endpoint; in Python this is usually not what you want. Do not supply the 'int' and 'default' arguments. """ # This code is a bit messy to make it fast for the # common case while still doing adequate error checking. istart = int(start) if istart != start: raise ValueError, "non-integer arg 1 for randrange()" if stop is default: if istart > 0: return int(self.random() * istart) raise ValueError, "empty range for randrange()" # stop argument supplied. istop = int(stop) if istop != stop: raise ValueError, "non-integer stop for randrange()" if step == 1 and istart < istop: try: return istart + int(self.random() * (istop - istart)) except OverflowError: # This can happen if istop-istart > sys.maxint + 1, and # multiplying by random() doesn't reduce it to something # <= sys.maxint. We know that the overall result fits # in an int, and can still do it correctly via math.floor(). # But that adds another function call, so for speed we # avoided that whenever possible. return int(istart + _floor(self.random() * (istop - istart))) if step == 1: raise ValueError, "empty range for randrange()" # Non-unit step argument supplied. istep = int(step) if istep != step: raise ValueError, "non-integer step for randrange()" if istep > 0: n = (istop - istart + istep - 1) / istep elif istep < 0: n = (istop - istart + istep + 1) / istep else: raise ValueError, "zero step for randrange()" if n <= 0: raise ValueError, "empty range for randrange()" return istart + istep * int(self.random() * n)
def _eng_notation(value, precision, delimiter, strip_zeros, _): ''' engineering notation ref: http://www.mathsisfun.com/definitions/engineering-notation.html returns a string of value with the proper precision and 10s exponent that is divisible by 3 delimiter is placed between the decimal value and 10s exponent strip_zeros - if true, trailing decimal zeros will be removed. ''' is_neg, sig_digits, dot_power, ten_power = _sci_decompose(value, precision) eng_power = int(3 * _floor(ten_power / 3)) eng_dot = dot_power + ten_power - eng_power return ('-' if is_neg else '') + _place_dot( sig_digits, eng_dot, strip_zeros) + delimiter + str(eng_power)
def updIB(self): """updates the inner border of the cluster. for each point inside the cluster scans the related reversed rank list for internal points.""" self.IB.clear() for p in self.points: limit = 0 if sys.version_info[0:2] >= (2, 4): for elem in reversed(rank_list[p]): if limit < q and elem[1] in self.points: self.IB.add(elem[1]) limit += 1 else: for i in range(len(rank_list[p]))[::-1]: if limit < q and rank_list[p][i][1] in self.points: self.IB.add(rank_list[p][i][1]) limit += 1 self.ib = _floor(_sqrt(len(self.IB))).__int__()
def randrange(self, start, stop = None, step = 1, int = int, default = None): istart = int(start) if istart != start: raise ValueError, 'non-integer arg 1 for randrange()' if stop is default: if istart > 0: return int(self.random() * istart) raise ValueError, 'empty range for randrange()' istop = int(stop) if istop != stop: raise ValueError, 'non-integer stop for randrange()' if step == 1 and istart < istop: try: return istart + int(self.random() * (istop - istart)) except OverflowError: return int(istart + _floor(self.random() * (istop - istart))) if step == 1: raise ValueError, 'empty range for randrange()' istep = int(step) if istep != step: raise ValueError, 'non-integer step for randrange()' if istep > 0: n = ((istop - istart) + istep - 1) / istep elif istep < 0: n = ((istop - istart) + istep + 1) / istep else: raise ValueError, 'zero step for randrange()' if n <= 0: raise ValueError, 'empty range for randrange()' return istart + istep * int(self.random() * n)
def smooth_block(block_mats, window=7, cutoff=0.5): """ Given an ordered ensemble of block matrices, uses a smoothing method to ensure hierarchical consistency. Arguments --------- block_mats : A three dimensional array, the first dimension of which is the ensemble index and the remaining two are square. Keyword Arguments ----------------- window : The window to be used in the smoothing technique. cutoff : The cutoff for whether a smoothed value should be set to 0 or 1. Output ------ new_block_mats : A three-dimensional array the same shape as block_mats. """ height = block_mats.shape[0] width = block_mats.shape[1] indices = _reduce(lambda x, y: x + y, [[ _np.arange(max(0, j - _floor(window / 2)), min(height, j + _ceil(window / 2)))[0], _np.arange(max(0, j - 2), min(height, j + 3))[-1] ] for j in range(height)], []) sums = _np.add.reduceat(block_mats, indices, axis=0)[0::2] nums = _np.add.reduceat(_np.ones([height]), indices, axis=0)[0::2] bmats_avg = sums / _np.outer(nums, _np.ones([width, width])).reshape( [height, width, width]) upper = (bmats_avg > cutoff).astype(float) new_bmats = _np.stack([(_la.expm(upper[j]) > 0).astype(float) for j in range(height)]) result = _np.zeros([height, width, width]) result[-1] = new_bmats[-1] for j in range(height - 1): result[height - j - 2] = new_bmats[height - j - 2] * result[height - j - 1] return result
def _mslicelen(start, stop, step): if stop is end or stop is None: return sys.maxint return int(_floor(stop - start) / step + 1)
def is_whole(num): """Determines if a number is whole, in other words, an integer. Can be used to determine if the sqrt of something is whole. """ return True if num == _floor(num) else False
class RandomDotOrg(Random): """Alternate random number generator using random.org as the source. Requires Internet access.""" __version__ = '0.1.0' _intMin = -1000000000 _intMax = 1000000000 _maxWidth = _intMax - _intMin + 1 _hexMax = int(_floor(_log(_intMax, 16))) _bitsMax = _hexMax << 2 _bitsMaxInt = 2**_bitsMax - 1 _fetchMax = 10000 def _fetch(self, service, **kwargs): "Fetch data from the Random.org HTTP Interface" url = 'https://www.random.org/%s/?' % urlquote(service) options = dict(format='plain') options.update(kwargs) headers = { 'User-Agent': 'RandomSources.randomDotOrg/%s' % self.__version__ } req = Request(url + urlencode(options), headers=headers) return urlopen(req).read().splitlines() def fetchHex(self, digits, rnd='new'): remainderDigits = digits % self._hexMax fullInts = digits // self._hexMax remainderFetch = fullInts % self._fetchMax fullFetches = fullInts // self._fetchMax r = '' options = dict(col=1, base=16, min=0, max=self._bitsMaxInt, num=self._fetchMax, rnd=rnd) if fullFetches > 0: r += ''.join(''.join(self._fetch('integers', **options)) for i in xrange(fullFetches)) if remainderFetch > 0: options['num'] = remainderFetch r += ''.join(self._fetch('integers', **options)) if remainderDigits > 0: options['max'] = 16**remainderDigits - 1 options['num'] = 1 r += self._fetch('integers', **options)[0] return r def fetchIntegers(self, imin, imax, num, rnd='new'): fullFetches = num // self._fetchMax remainderFetch = num % self._fetchMax r = [] options = dict(col=1, base=10, min=imin, max=imax, num=self._fetchMax, rnd=rnd) for i in xrange(num // self._fetchMax): r.extend(map(int, self._fetch('integers', **options))) if remainderFetch > 0: options['num'] = remainderFetch r.extend(map(int, self._fetch('integers', **options))) return r def checkBitQuota(self): return int(self._fetch('quota', format='plain')[0]) def random(self, n=1): if n == 1: return self.getrandbits(bitsPerFloat) * 2**-bitsPerFloat return [ x * 2**-bitsPerFloat for x in self.getrandbits(bitsPerFloat, n) ] random.__doc__ = Random.random.__doc__ def getrandbits(self, k, n=1): "getrandbits(k) -> x. Generates a long int with k random bits." if k == 0: return 0 hexString = self.fetchHex((k * n + 3) // 4) result = long(hexString, 16) filter = 2**k - 1 if n == 1: return result & filter r = [] for i in xrange(n): r.append(result & filter) result >>= k return r def _stub(self, *args, **kwargs): "Stub method. Not used for a remote random number generator." return None seed = _stub jumpahead = _stub def _notimplemented(self, *args, **kwargs): "Method should not be called for a remote random number generator." raise NotImplementedError('Remote entropy sources do not have state.') getstate = _notimplemented setstate = _notimplemented ## -------------------- integer methods ------------------- def randrange(self, start, stop=None, step=1, n=1): # This function exactly parallels the code in Random.py, # with the one additional feature of fetching multiple # requests together; most comments are copied here. imin = self._intMin maxwidth = self._maxWidth # This code is a bit messy to make it fast for the # common case while still doing adequate error checking. istart = int(start) if istart != start: raise ValueError('non-integer arg 1 for randrange()') if stop is None: if istart > 0: if istart > maxwidth: if n > 1: return self._randbelow(istart, n) return self._randbelow(istart) r = self.fetchIntegers(imin, imin + istart - 1, n) if n == 1: return r[0] return r raise ValueError('empty range for randrange()') # stop argument supplied. istop = int(stop) if istop != stop: raise ValueError('non-integer stop for randrange()') width = istop - istart if step == 1 and width > 0: if width >= maxwidth: if n > 1: return [int(istart + x) for x in self._randbelow(width, n)] return int(istart + self._randbelow(width)) shift = istart - imin r = self.fetchIntegers(imin, imin + width - 1, n) if n == 1: return shift + r[0] return [int(shift + x) for x in r] if step == 1: raise ValueError('empty range for randrange() (%d,%d, %d)' % (istart, istop, width)) # Non-unit step argument supplied. istep = int(step) if istep != step: raise ValueError('non-integer step for randrange()') if istep > 0: size = (width + istep - 1) // istep elif istep < 0: size = (width + istep + 1) // istep else: raise ValueError('zero step for randrange()') if size <= 0: raise ValueError('empty range for randrange()') if size >= maxwidth: if n > 1: return [ int(istart + istep * x) for x in self._randbelow(size, n) ] return int(istart + istep * self._randbelow(size)) shift = istart - istep * imin r = self.fetchIntegers(imin, imin + size - 1, n) if n == 1: return int(shift + istep * r[0]) return [int(shift + istep * x) for x in r] randrange.__doc__ = Random.randrange.__doc__ def randint(self, a, b, n=1): return self.randrange(a, b + 1, n=n) randint.__doc__ = Random.randint.__doc__ def _randbelow(self, n, num=1, _log=_log, int=int): k = int(1.00001 + _log(n - 1, 2.0)) # 2**k > n-1 > 2**(k-2) if num == 1: r = self.getrandbits(k) while r >= n: r = self.getrandbits(k) return r r = [] filter = (1 << k) - 1 while num > 0: bits = self.getrandbits(k * num) for i in xrange(num): x = bits & filter if x < n: r.append(x) num -= 1 return r _randbelow.__doc__ = Random._randbelow.__doc__ ## -------------------- sequence methods ------------------- def choice(self, seq, n=1): length = len(seq) if length == 0: raise IndexError('list index out of range') if n == 1: return seq[self.randrange(length)] return [seq[i] for i in self.randrange(length, n=n)] choice.__doc__ = Random.choice.__doc__ def shuffle(self, x, random=None): if random is not None: return Random.shuffle(self, x, random) randrange = self.randrange for i in reversed(xrange(1, len(x))): # pick an element in x[:i+1] with which to exchange x[i] j = randrange(i + 1) x[i], x[j] = x[j], x[i] shuffle.__doc__ = Random.shuffle.__doc__ def sample(self, population, k): # This function exactly parallels the code in Random.py. # Comments are therefore omitted, to save space. n = len(population) if not 0 <= k <= n: raise ValueError('sample larger than population') randrange = self.randrange result = [None] * k setsize = 21 if k > 5: setsize += 4**_ceil(_log(k * 3, 4)) if n <= setsize or hasattr(population, 'keys'): pool = list(population) for i in xrange(k): j = randrange(n - i) result[i] = pool[j] pool[j] = pool[n - i - 1] else: try: selected = set() selected_add = selected.add for i in xrange(k): j = randrange(n) while j in selected: j = randrange(n) selected_add(j) result[i] = population[j] except (TypeError, KeyError): if isinstance(population, list): raise return self.sample(tuple(population), k) return result sample.__doc__ = Random.sample.__doc__
def binomialvariate(self, n=1, p=0.5): """Binomial random variable. Gives the number of successes for *n* independent trials with the probability of success in each trial being *p*: sum(random() < p for i in range(n)) Returns an integer in the range: 0 <= X <= n """ # Error check inputs and handle edge cases if n < 0: raise ValueError("n must be non-negative") if p <= 0.0 or p >= 1.0: if p == 0.0: return 0 if p == 1.0: return n raise ValueError("p must be in the range 0.0 <= p <= 1.0") random = self.random # Fast path for a common case if n == 1: return _index(random() < p) # Exploit symmetry to establish: p <= 0.5 if p > 0.5: return n - self.binomialvariate(n, 1.0 - p) if n * p < 10.0: # BG: Geometric method by Devroye with running time of O(np). # https://dl.acm.org/doi/pdf/10.1145/42372.42381 x = y = 0 c = _log(1.0 - p) if not c: return x while True: y += _floor(_log(random()) / c) + 1 if y > n: return x x += 1 # BTRS: Transformed rejection with squeeze method by Wolfgang Hörmann # https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8407&rep=rep1&type=pdf assert n * p >= 10.0 and p <= 0.5 setup_complete = False spq = _sqrt(n * p * (1.0 - p)) # Standard deviation of the distribution b = 1.15 + 2.53 * spq a = -0.0873 + 0.0248 * b + 0.01 * p c = n * p + 0.5 vr = 0.92 - 4.2 / b while True: u = random() v = random() u -= 0.5 us = 0.5 - _fabs(u) k = _floor((2.0 * a / us + b) * u + c) if k < 0 or k > n: continue # The early-out "squeeze" test substantially reduces # the number of acceptance condition evaluations. if us >= 0.07 and v <= vr: return k # Acceptance-rejection test. # Note, the original paper errorneously omits the call to log(v) # when comparing to the log of the rescaled binomial distribution. if not setup_complete: alpha = (2.83 + 5.1 / b) * spq lpq = _log(p / (1.0 - p)) m = _floor((n + 1) * p) # Mode of the distribution h = _lgamma(m + 1) + _lgamma(n - m + 1) setup_complete = True # Only needs to be done once v *= alpha / (a / (us * us) + b) if _log(v) <= h - _lgamma(k + 1) - _lgamma(n - k + 1) + (k - m) * lpq: return k
def print_bench_results(self): self.out.write('==============================\n') self.out.write(' *** BENCHMARKING RESULTS *** \n') self.out.write('==============================\n') self.out.write('\n') # benchname, time, benchtitle results = [] for item, outcome in self._memo: if isinstance(item, Item): best = item.benchtime if best is None: # skipped or failed benchmarks tstr = '---' else: # from IPython.Magic.magic_timeit if best > 0.0: order = min(-int(_floor(log10(best)) // 3), 3) else: order = 3 tstr = "%.*g %s" % (precision, best * scaling[order], units[order]) results.append( [item.name, tstr, item.benchtitle] ) # dot/unit align second column # FIXME simpler? this is crappy -- shame on me... wm = [0]*len(units) we = [0]*len(units) for s in results: tstr = s[1] n,u = tstr.split() # unit n un = unitn[u] try: m,e = n.split('.') except ValueError: m,e = n,'' wm[un] = max(len(m), wm[un]) we[un] = max(len(e), we[un]) for s in results: tstr = s[1] n,u = tstr.split() un = unitn[u] try: m,e = n.split('.') except ValueError: m,e = n,'' m = m.rjust(wm[un]) e = e.ljust(we[un]) if e.strip(): n = '.'.join((m,e)) else: n = ' '.join((m,e)) # let's put the number into the right place txt = '' for i in range(len(units)): if i == un: txt += n else: txt += ' '*(wm[i]+we[i]+1) s[1] = '%s %s' % (txt, u) # align all columns besides the last one for i in range(2): w = max(len(s[i]) for s in results) for s in results: s[i] = s[i].ljust(w) # show results for s in results: self.out.write('%s | %s | %s\n' % tuple(s))
def floor(x): return _floor(x)
def print_bench_results(self): self.out.write("==============================\n") self.out.write(" *** BENCHMARKING RESULTS *** \n") self.out.write("==============================\n") self.out.write("\n") # benchname, time, benchtitle results = [] for item, outcome in self._memo: if isinstance(item, Item): best = item.benchtime if best is None: # skipped or failed benchmarks tstr = "---" else: # from IPython.Magic.magic_timeit if best > 0.0: order = min(-int(_floor(log10(best)) // 3), 3) else: order = 3 tstr = "%.*g %s" % (precision, best * scaling[order], units[order]) results.append([item.name, tstr, item.benchtitle]) # dot/unit align second column # FIXME simpler? this is crappy -- shame on me... wm = [0] * len(units) we = [0] * len(units) for s in results: tstr = s[1] n, u = tstr.split() # unit n un = unitn[u] try: m, e = n.split(".") except ValueError: m, e = n, "" wm[un] = max(len(m), wm[un]) we[un] = max(len(e), we[un]) for s in results: tstr = s[1] n, u = tstr.split() un = unitn[u] try: m, e = n.split(".") except ValueError: m, e = n, "" m = m.rjust(wm[un]) e = e.ljust(we[un]) if e.strip(): n = ".".join((m, e)) else: n = " ".join((m, e)) # let's put the number into the right place txt = "" for i in range(len(units)): if i == un: txt += n else: txt += " " * (wm[i] + we[i] + 1) s[1] = "%s %s" % (txt, u) # align all columns besides the last one for i in range(2): w = max(len(s[i]) for s in results) for s in results: s[i] = s[i].ljust(w) # show results for s in results: self.out.write("%s | %s | %s\n" % tuple(s))
def _mslicelen(start, stop, step): if stop is end or stop is None: return sys.maxint return int(_floor(stop-start)/step + 1)
def exec(self): requested_width = int(self.arg('width')) requested_height = int(self.arg('height')) p1 = self.arg('p1') p2 = self.arg('p2') filename = self.arg('filename') uid = 'file_image:' + _path.splitext(filename)[0] try: img_file = _file.get(uid) # type: _model.ImageFile except _file.error.FileNotFound as e: raise self.not_found(str(e)) # Align side lengths and redirect aligned_width = _api.align_image_side( requested_width, _api.get_image_resize_limit_width()) aligned_height = _api.align_image_side( requested_height, _api.get_image_resize_limit_height()) if aligned_width != requested_width or aligned_height != requested_height: redirect = _router.rule_url( 'file_storage_odm@image', { 'width': aligned_width, 'height': aligned_height, 'p1': p1, 'p2': p2, 'filename': filename, }) return self.redirect(redirect, 301) # Original size orig_width = img_file.width orig_height = img_file.height orig_ratio = orig_width / orig_height need_resize = True # Calculate new size if not requested_width and not requested_height: # No resize needed, return original image resize_width = orig_width resize_height = orig_height need_resize = False elif requested_width and not requested_height: # Resize by width, preserve aspect ration resize_width = requested_width resize_height = _floor(requested_width / orig_ratio) elif requested_height and not requested_width: # Resize by height, preserve aspect ration resize_width = _floor(requested_height * orig_ratio) resize_height = requested_height else: # Exact resizing resize_width = requested_width resize_height = requested_height # Checking source file storage_path = img_file.get_field('storage_path') if not _path.exists(storage_path): return self.redirect('http://placehold.it/{}x{}'.format( requested_width, requested_height)) # Calculating target file location static_path = _path.join(_reg.get('paths.static'), 'image', 'resize', str(requested_width), str(requested_height), p1, p2, filename) # Create target directory target_dir = _path.dirname(static_path) if not _path.exists(target_dir): _makedirs(target_dir, 0o755, True) if not _path.exists(static_path): # Open source image img = _Image.open(storage_path) # type: _Image # Resize if need_resize: # Crop crop_ratio = resize_width / resize_height crop_width = orig_width crop_height = _floor(crop_width / crop_ratio) crop_top = _floor(orig_height / 2) - _floor(crop_height / 2) crop_left = 0 if crop_height > orig_height: crop_height = orig_height crop_width = _floor(crop_height * crop_ratio) crop_top = 0 crop_left = _floor(orig_width / 2) - _floor(crop_width / 2) crop_right = crop_left + crop_width crop_bottom = crop_top + crop_height cropped = img.crop( (crop_left, crop_top, crop_right, crop_bottom)) img.close() # Resize img = cropped.resize((resize_width, resize_height), _Image.BILINEAR) img.save(static_path) img.close() return self.redirect( img_file.get_url(width=requested_width, height=requested_height))